webui.py 556 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279
  1. from nicegui import ui, app
  2. import sys, os, json, subprocess, importlib, re, threading, signal
  3. import traceback
  4. import time
  5. import asyncio
  6. from urllib.parse import urljoin
  7. from pathlib import Path
  8. # from functools import partial
  9. from utils.my_log import logger
  10. from utils.config import Config
  11. from utils.common import Common
  12. from utils.audio import Audio
  13. """
  14. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  15. @@@@@@@@@@@@@@@.:;;;++;;;;:,@@@@@@@@@@@@@@@@@@@@@@
  16. @@@@@@@@@@@@@@:;+++++;;++++;;;.@@@@@@@@@@@@@@@@@@@
  17. @@@@@@@@@@@@@:++++;;;;;;;;;;+++;,@@@@@@@@@@@@@@@@@
  18. @@@@@@@@@@@.;+++;;;;;;;;;;;;;;++;:@@@@@@@@@@@@@@@@
  19. @@@@@@@@@@;+++;;;;;;;;;;;;;;;;;;++;:@@@@@@@@@@@@@@
  20. @@@@@@@@@:+++;;;;;;;;;;;;;;;;;;;;++;.@@@@@@@@@@@@@
  21. @@@@@@@@;;+;;;;;;;;;;;;;;;;;;;;;;;++:@@@@@@@@@@@@@
  22. @@@@@@@@;+;;;;:::;;;;;;;;;;;;;;;;:;+;,@@@@@@@@@@@@
  23. @@@@@@@:+;;:;;:::;:;;:;;;;::;;:;:::;+;.@@@@@@@@@@@
  24. @@@@@@.;+;::;:,:;:;;+:++:;:::+;:::::++:+@@@@@@@@@@
  25. @@@@@@:+;;:;;:::;;;+%;*?;;:,:;*;;;;:;+;:@@@@@@@@@@
  26. @@@@@@;;;+;;+;:;;;+??;*?++;,:;+++;;;:++:@@@@@@@@@@
  27. @@@@@.++*+;;+;;;;+?;?**??+;:;;+.:+;;;;+;;@@@@@@@@@
  28. @@@@@,+;;;;*++*;+?+;**;:?*;;;;*:,+;;;;+;,@@@@@@@@@
  29. @@@@@,:,+;+?+?++?+;,?#%*??+;;;*;;:+;;;;+:@@@@@@@@@
  30. @@@@@@@:+;*?+?#%;;,,?###@#+;;;*;;,+;;;;+:@@@@@@@@@
  31. @@@@@@@;+;??+%#%;,,,;SSS#S*+++*;..:+;?;+;@@@@@@@@@
  32. @@@@@@@:+**?*?SS,,,,,S#S#+***?*;..;?;**+;@@@@@@@@@
  33. @@@@@@@:+*??*??S,,,,,*%SS+???%++;***;+;;;.@@@@@@@@
  34. @@@@@@@:*?*;*+;%:,,,,;?S?+%%S?%+,:?;+:,,,@@@@@@@@
  35. @@@@@@@,*?,;+;+S:,,,,%?+;S%S%++:+??+:,,,:@@@@@@@@
  36. @@@@@@@,:,@;::;+,,,,,+?%*+S%#?*???*;,,,,,.@@@@@@@@
  37. @@@@@@@@:;,::;;:,,,,,,,,,?SS#??*?+,.,,,:,@@@@@@@@@
  38. @@@@@@;;+;;+:,:%?%*;,,,,SS#%*??%,.,,,,,:@@@@@@@@@
  39. @@@@@.+++,++:;???%S?%;.+#####??;.,,,,,,:@@@@@@@@@
  40. @@@@@:++::??+S#??%#??S%?#@#S*+?*,,,,,,:,@@@@@@@@@@
  41. @@@@@:;;:*?;+%#%?S#??%SS%+#%..;+:,,,,,,@@@@@@@@@@@
  42. @@@@@@,,*S*;?SS?%##%?S#?,.:#+,,+:,,,,,,@@@@@@@@@@@
  43. @@@@@@@;%?%#%?*S##??##?,..*#,,+:,,;*;.@@@@@@@@@@@
  44. @@@@@@.*%??#S*?S#@###%;:*,.:#:,+;:;*+:@@@@@@@@@@@@
  45. @@@@@@,%S??SS%##@@#%S+..;;.,#*;???*?+++:@@@@@@@@@@
  46. @@@@@@:S%??%####@@S,,*,.;*;+#*;+?%??#S%+.@@@@@@@@@
  47. @@@@@@:%???%@###@@?,,:**S##S*;.,%S?;+*?+.,..@@@@@@
  48. @@@@@@;%??%#@###@@#:.;@@#@%%,.,%S*;++*++++;.@@@@@
  49. @@@@@@,%S?S@@###@@@%+#@@#@?;,.:?;??++?%?***+.@@@@@
  50. @@@@@@.*S?S####@@####@@##@?..:*,+:??**%+;;;;..@@@@
  51. @@@@@@:+%?%####@@####@@#@%;:.;;:,+;?**;++;,:;:,@@@
  52. @@@@@@;;*%?%@##@@@###@#S#*:;*+,;.+***?******+:.@@@
  53. @@@@@@:;:??%@###%##@#%++;+*:+;,:;+%?*;+++++;:.@@@@
  54. @@@@@@.+;:?%@@#%;+S*;;,:::**+,;:%??*+.@....@@@@@@@
  55. @@@@@@@;*::?#S#S+;,..,:,;:?+?++*%?+::@@@@@@@@@@@@@
  56. @@@@@@@.+*+++?%S++...,;:***??+;++:.@@@@@@@@@@@@@@@
  57. @@@@@@@@:::..,;+*+;;+*?**+;;;+;:.@@@@@@@@@@@@@@@@@
  58. @@@@@@@@@@@@@@@,+*++;;:,..@@@@@@@@@@@@@@@@@@@@@@@@
  59. @@@@@@@@@@@@@@@@::,.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  60. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  61. """
  62. """
  63. 全局变量
  64. """
  65. user_info = None
  66. # 创建一个全局变量,用于表示程序是否正在运行
  67. running_flag = False
  68. # 定义一个标志变量,用来追踪定时器的运行状态
  69. loop_screenshot_timer_running = False
  70. loop_screenshot_timer = None
  71. common = None
  72. config = None
  73. audio = None
  74. my_handle = None
  75. config_path = None
  76. # 存储运行的子进程
  77. my_subprocesses = {}
  78. # 本地启动的web服务,用来加载本地的live2d
  79. web_server_port = 12345
  80. # 聊天记录计数
  81. scroll_area_chat_box_chat_message_num = 0
  82. # 聊天记录最多保留100条
  83. scroll_area_chat_box_chat_message_max_num = 100
  84. """
  85. 初始化基本配置
  86. """
  87. def init():
  88. """
  89. 初始化基本配置
  90. """
  91. global config_path, config, common, audio
  92. common = Common()
  93. if getattr(sys, 'frozen', False):
  94. # 当前是打包后的可执行文件
  95. bundle_dir = Path(getattr(sys, '_MEIPASS', Path(sys.executable).parent))
  96. file_relative_path = bundle_dir.resolve()
  97. else:
  98. # 当前是源代码
  99. file_relative_path = Path(__file__).parent.resolve()
  100. # logger.info(file_relative_path)
  101. # 初始化文件夹
  102. def init_dir():
  103. # 创建日志文件夹
  104. log_dir = file_relative_path / 'log'
  105. # mkdir 方法的 parents=True 参数可以确保父目录的创建(如有必要),exist_ok=True 则避免在目录已存在时抛出异常。
  106. log_dir.mkdir(parents=True, exist_ok=True)
  107. # 创建音频输出文件夹
  108. audio_out_dir = file_relative_path / 'out'
  109. audio_out_dir.mkdir(parents=True, exist_ok=True)
  110. init_dir()
  111. logger.debug("项目相关文件夹初始化完成")
  112. # 配置文件路径
  113. config_path = file_relative_path / 'config.json'
  114. config_path = str(config_path)
  115. logger.debug("配置文件路径=" + str(config_path))
  116. # 实例化音频类
  117. audio = Audio(config_path, type=2)
  118. # 实例化配置类
  119. config = Config(config_path)
  120. # 初始化基本配置
  121. init()
  122. # 将本地目录中的静态文件(如 CSS、JavaScript、图片等)暴露给 web 服务器,以便用户可以通过特定的 URL 访问这些文件。
  123. if config.get("webui", "local_dir_to_endpoint", "enable"):
  124. for tmp in config.get("webui", "local_dir_to_endpoint", "config"):
  125. app.add_static_files(tmp['url_path'], tmp['local_dir'])
  126. # 暗夜模式
  127. dark = ui.dark_mode()
  128. """
  129. 通用函数
  130. """
  131. def textarea_data_change(data):
  132. """
  133. 字符串数组数据格式转换
  134. """
  135. tmp_str = ""
  136. if data is not None:
  137. for tmp in data:
  138. tmp_str = tmp_str + tmp + "\n"
  139. return tmp_str
  140. """
  141. .@@@@@ @@@@@.
  142. .@@@@@ @@@@@.
  143. ]]]]] .]]]]` .]]]]` ,]@@@@@\` .@@@@@,/@@@\` .]]]]] ]]]]]` ]]]]].
  144. =@@@@^ =@@@@@` =@@@@. =@@@@@@@@@@@\ .@@@@@@@@@@@@@ *@@@@@ @@@@@^ @@@@@.
  145. =@@@@ ,@@@@@@@ .@@@@` =@@@@^ =@@@@^ .@@@@@` =@@@@^ *@@@@@ @@@@@^ @@@@@.
  146. @@@@^@@@@\@@@^=@@@^ @@@@@@@@@@@@@@@ .@@@@@ =@@@@@ *@@@@@ @@@@@^ @@@@@.
  147. ,@@@@@@@^ \@@@@@@@ =@@@@^ .@@@@@. =@@@@^ *@@@@@ .@@@@@^ @@@@@.
  148. =@@@@@@ .@@@@@@. \@@@@@]/@@@@@` .@@@@@@]/@@@@@. .@@@@@@@@@@@@@^ @@@@@.
  149. \@@@@` =@@@@^ ,\@@@@@@@@[ .@@@@^\@@@@@[ .\@@@@@[=@@@@^ @@@@@.
  150. """
  151. # 配置
  152. webui_ip = config.get("webui", "ip")
  153. webui_port = config.get("webui", "port")
  154. webui_title = config.get("webui", "title")
  155. # CSS
  156. theme_choose = config.get("webui", "theme", "choose")
  157. tab_panel_css = config.get("webui", "theme", "list", theme_choose, "tab_panel")
  158. card_css = config.get("webui", "theme", "list", theme_choose, "card")
  159. button_bottom_css = config.get("webui", "theme", "list", theme_choose, "button_bottom")
  160. button_bottom_color = config.get("webui", "theme", "list", theme_choose, "button_bottom_color")
  161. button_internal_css = config.get("webui", "theme", "list", theme_choose, "button_internal")
  162. button_internal_color = config.get("webui", "theme", "list", theme_choose, "button_internal_color")
  163. switch_internal_css = config.get("webui", "theme", "list", theme_choose, "switch_internal")
  164. echart_css = config.get("webui", "theme", "list", theme_choose, "echart")
  165. def goto_func_page():
  166. """
  167. 跳转到功能页
  168. """
  169. global audio, my_subprocesses, config
  170. # 过期时间
  171. expiration_ts = None
  172. def start_programs():
  173. """根据配置启动所有程序。
  174. """
  175. global config
  176. for program in config.get("coordination_program"):
  177. if not program["enable"]:
  178. continue
  179. name = program["name"]
  180. executable = program["executable"] # Python 解释器的路径
  181. app_path = program["parameters"][0] # 假设第一个参数总是 app.py 的路径
  182. # 从 app.py 的路径中提取目录
  183. app_dir = os.path.dirname(app_path)
  184. # 使用 Python 解释器路径和 app.py 路径构建命令
  185. cmd = [executable, app_path]
  186. logger.info(f"运行程序: {name} 位于: {app_dir}")
  187. # 在 app.py 文件所在的目录中启动程序
  188. process = subprocess.Popen(cmd, cwd=app_dir, shell=True)
  189. my_subprocesses[name] = process
  190. name = "main"
  191. # 根据操作系统的不同,微调参数
  192. if common.detect_os() in ['Linux', 'MacOS']:
  193. process = subprocess.Popen(["python", f"main.py"], shell=False)
  194. else:
  195. process = subprocess.Popen(["python", f"main.py"], shell=True)
  196. my_subprocesses[name] = process
  197. logger.info(f"运行程序: {name}")
  198. def stop_program(name):
  199. """停止一个正在运行的程序及其所有子进程,兼容 Windows、Linux 和 macOS。
  200. Args:
  201. name (str): 要停止的程序的名称。
  202. """
  203. if name in my_subprocesses:
  204. pid = my_subprocesses[name].pid # 获取进程ID
  205. logger.info(f"停止程序和它所有的子进程: {name} with PID {pid}")
  206. try:
  207. if os.name == 'nt': # Windows
  208. command = ["taskkill", "/F", "/T", "/PID", str(pid)]
  209. subprocess.run(command, check=True)
  210. else: # POSIX系统,如Linux和macOS
  211. os.killpg(os.getpgid(pid), signal.SIGKILL)
  212. logger.info(f"程序 {name} 和 它所有的子进程都被终止.")
  213. except Exception as e:
  214. logger.error(f"终止程序 {name} 失败: {e}")
  215. del my_subprocesses[name] # 从进程字典中移除
  216. else:
  217. logger.warning(f"程序 {name} 没有在运行.")
  218. def stop_programs():
  219. """根据配置停止所有程序。
  220. """
  221. global config
  222. for program in config.get("coordination_program"):
  223. if not program["enable"]:
  224. continue
  225. stop_program(program["name"])
  226. stop_program("main")
  227. def check_expiration():
  228. try:
  229. import requests
  230. API_URL = urljoin(config.get("login", "ums_api"), '/auth/check_expiration')
  231. if user_info is None:
  232. ui.notify(position="top", type="negative", message=f"账号登录信息失效,请重新登录")
  233. stop_programs()
  234. return False
  235. if "accessToken" not in user_info:
  236. ui.notify(position="top", type="negative", message=f"账号登录信息失效,请重新登录")
  237. stop_programs()
  238. return False
  239. headers = {
  240. "Authorization": "Bearer " + user_info["accessToken"]
  241. }
  242. # 发送 POST 请求
  243. response = requests.post(API_URL, headers=headers)
  244. # 判断状态码
  245. if response.status_code == 200:
  246. resp_json = response.json()
  247. if resp_json["code"] == 0 and resp_json["success"]:
  248. remainder = common.time_difference_in_seconds(resp_json["data"]["expiration_ts"])
  249. logger.info(f'账号可用,过期时间:{resp_json["data"]["expiration_ts"]}')
  250. return True
  251. else:
  252. remainder = common.time_difference_in_seconds(resp_json["data"]["expiration_ts"])
  253. ui.notify(position="top", type="negative", message=f'账号过期时间:{resp_json["data"]["expiration_ts"]},已过期:{remainder}秒,请联系管理员续费')
  254. logger.error(f'账号过期时间:{resp_json["data"]["expiration_ts"]},已过期:{remainder}秒,请联系管理员续费')
  255. stop_programs()
  256. return False
  257. # elif response.status_code == 401:
  258. # ui.notify(position="top", type="negative", message=f"账号已到期,请联系管理员续费")
  259. # logger.error(f"账号已到期,请联系管理员续费")
  260. # stop_programs()
  261. # return False
  262. else:
  263. logger.error(f"自检异常!")
  264. return False
  265. except Exception as e:
  266. ui.notify(position="top", type="negative", message=f"错误:{e}")
  267. logger.error(traceback.format_exc())
  268. return False
  269. if config.get("login", "enable"):
  270. # 十分钟一次的检测
  271. ui.timer(600.0, lambda: check_expiration())
  272. """
  273. =@@^ ,@@@^ .@@@. ..... =@@. ]@\ ,]]]]]]]]]]]]]]]. .]]]]]]]]]]]]]]]]]]]] ,]]]]]]]]]]]]]]]]]` ,/. @@@^ /] ,@@@.
  274. =@@^ .@@@@@@@@@@@@@@^ /@@\]]@@@@@=@@@@@@@@@. \@@@`=@@@@@@@@@@@@@@@. .@@@@@@@@@@@@@@@@@@@@ =@@@@@@@@@@@@@@@@@^ .\@@^@@@\@@@`.@@@^
  275. @@@@@@@^@@@@@@@@@@@@@@^ =@@@@@^ =@@\]]]/@@]]@@]. =@/`=@@^ .@@@ .@@@. .@@@^ @@@^ =@@@ ,/@@@@/` =@@@@@@@@@@@^=@@@@@@@@@.
  276. @@@@@@@^@@@^@@\` =@@^.@@@]]]`=@@^=@@@@@@@@@@@.]]]]` =@@^=@@@@@@@^@@@. .@@@\]]]]@@@\]]]]/@@@ @@@\/@\..@@@@[./@/@@@. ,[[\@@@@/[[[\@@@`..@@@`
  277. =@@^ ,]]]/@@@]]]]]]]].\@@@@@^@@@OO=@@@@@@@@@..@@@@^ =@@^]]]@@@]]`@@@. .@@@@@@@@@@@@@@@@@@@@ @@@^=@@@^@@@^/@@@\@@@..]@@@@@@@@@@]@@@@^ .@@@.
  278. =@@@@=@@@@@@@@@@@@@@@. =@@^ .OO@@@.[[\@@[[[[. =@@^ =@@^@@@@@@@@^@@@. .@@@^ @@@^ =@@@ @@@^ .`,]@@@^`,` =@@@. \@/.]@@@^,@@@@@@\ =@@^
  279. .@@@@@@@. .@@@` /@@/ .@@@@@@@,.=@@=@@@@@@@@@^ =@@^,=@@^=@@@@@@@.@@@. .@@@\]]]]@@@\]]]]/@@@ @@@^]@@@@@@@@@@@]=@@@. ]]]@@@\]]]]] .=@@\@@@.
  280. @@\@@^ .@@@\. /@@@. =@@^ =@\@@^.../@@..... =@@@@=@@^=@@[[\@@.@@@. .@@@@@@@@@@@@@@@@@@@@ @@@@@@/..@@@^,@@@@@@@. O@@@@@@@@@@@ .@@@@@^
  281. =@@^ ,\@@@@@@@@. =@@^/^\@@@`@@@@@@@@@@^ /@@@/@@@`=@@OO@@@.@@@. =@@@` @@@^ =@@@ @@@^ \@@@@@^ .=@@@. .@@@@\`/@@/ /@@@\.
  282. =@@^ ,/@@@@@@@@] =@@@@^/@@@@]` =@@. .\@/.=@@@ =@@[[[[[.@@@. /@@@ @@@^ ./@@@ @@@^.............=@@@. O@@@@@@\`,/@@@@@@@@`
  283. @@@@@^.@@@@@@@/..[@@@@/. ,@@`/@@@`[@@@@@@@@@@@@. /@@@^ =@@@@@@. /@@@^ @@@^,@@@@@@^ @@@@@@@@@@@@@@@@@@@@@..\@@@@@[,\@@\@@@@` ,@@@^
  284. ,[[[. .O[[. [` ,/ ...... ,^ .[[[[` ,` .... [[[[` ,[[[. .[. ,/. .`
  285. """
  286. # 创建一个函数,用于运行外部程序
  287. def run_external_program(config_path="config.json", type="webui"):
  288. global running_flag
  289. if running_flag:
  290. if type == "webui":
  291. ui.notify(position="top", type="warning", message="运行中,请勿重复运行")
  292. return
  293. try:
  294. running_flag = True
  295. # 启动协同程序和主程序
  296. start_programs()
  297. if type == "webui":
  298. ui.notify(position="top", type="positive", message="程序开始运行")
  299. logger.info("程序开始运行")
  300. return {"code": 200, "msg": "程序开始运行"}
  301. except Exception as e:
  302. if type == "webui":
  303. ui.notify(position="top", type="negative", message=f"错误:{e}")
  304. logger.error(traceback.format_exc())
  305. running_flag = False
  306. return {"code": -1, "msg": f"运行失败!{e}"}
  307. # 定义一个函数,用于停止正在运行的程序
  308. def stop_external_program(type="webui"):
  309. global running_flag
  310. if running_flag:
  311. try:
  312. # 停止协同程序
  313. stop_programs()
  314. running_flag = False
  315. if type == "webui":
  316. ui.notify(position="top", type="positive", message="程序已停止")
  317. logger.info("程序已停止")
  318. except Exception as e:
  319. if type == "webui":
  320. ui.notify(position="top", type="negative", message=f"停止错误:{e}")
  321. logger.error(f"停止错误:{e}")
  322. return {"code": -1, "msg": f"重启失败!{e}"}
  323. # 开关灯
  324. def change_light_status(type="webui"):
  325. if dark.value:
  326. button_light.set_text("关灯")
  327. else:
  328. button_light.set_text("开灯")
  329. dark.toggle()
  330. # 重启
  331. def restart_application(type="webui"):
  332. try:
  333. # 先停止运行
  334. stop_external_program(type)
  335. logger.info(f"重启webui")
  336. if type == "webui":
  337. ui.notify(position="top", type="ongoing", message=f"重启中...")
  338. python = sys.executable
  339. os.execl(python, python, *sys.argv) # Start a new instance of the application
  340. except Exception as e:
  341. logger.error(traceback.format_exc())
  342. return {"code": -1, "msg": f"重启失败!{e}"}
  343. # 恢复出厂配置
  344. def factory(src_path='config.json.bak', dst_path='config.json', type="webui"):
  345. # src_path = 'config.json.bak'
  346. # dst_path = 'config.json'
  347. try:
  348. with open(src_path, 'r', encoding="utf-8") as source:
  349. with open(dst_path, 'w', encoding="utf-8") as destination:
  350. destination.write(source.read())
  351. logger.info("恢复出厂配置成功!")
  352. if type == "webui":
  353. ui.notify(position="top", type="positive", message=f"恢复出厂配置成功!")
  354. # 重启
  355. restart_application()
  356. return {"code": 200, "msg": "恢复出厂配置成功!"}
  357. except Exception as e:
  358. logger.error(f"恢复出厂配置失败!\n{e}")
  359. if type == "webui":
  360. ui.notify(position="top", type="negative", message=f"恢复出厂配置失败!\n{e}")
  361. return {"code": -1, "msg": f"恢复出厂配置失败!\n{e}"}
  362. # openai 测试key可用性
  363. def test_openai_key():
  364. data_json = {
  365. "base_url": input_openai_api.value,
  366. "api_keys": textarea_openai_api_key.value,
  367. "model": select_chatgpt_model.value,
  368. "temperature": round(float(input_chatgpt_temperature.value), 1),
  369. "max_tokens": int(input_chatgpt_max_tokens.value),
  370. "top_p": round(float(input_chatgpt_top_p.value), 1),
  371. "presence_penalty": round(float(input_chatgpt_presence_penalty.value), 1),
  372. "frequency_penalty": round(float(input_chatgpt_frequency_penalty.value), 1),
  373. "preset": input_chatgpt_preset.value
  374. }
  375. resp_json = common.test_openai_key(data_json, 2)
  376. if resp_json["code"] == 200:
  377. ui.notify(position="top", type="positive", message=resp_json["msg"])
  378. else:
  379. ui.notify(position="top", type="negative", message=resp_json["msg"])
  380. # GPT-SoVITS加载模型
  381. async def gpt_sovits_set_model():
  382. try:
  383. if select_gpt_sovits_type.value == "v2_api_0821":
  384. async def set_gpt_weights():
  385. try:
  386. API_URL = urljoin(input_gpt_sovits_api_ip_port.value, '/set_gpt_weights?weights_path=' + input_gpt_sovits_gpt_model_path.value)
  387. # logger.debug(API_URL)
  388. resp_json = await common.send_async_request(API_URL, "GET", None, resp_data_type="json")
  389. if resp_json is None:
  390. content = f"gpt_weights:{input_gpt_sovits_gpt_model_path.value} 加载失败,请查看双方日志排查问题"
  391. logger.error(content)
  392. return False
  393. else:
  394. if resp_json["message"] == "success":
  395. content = f"gpt_weights:{input_gpt_sovits_gpt_model_path.value} 加载成功"
  396. logger.info(content)
  397. else:
  398. content = f"gpt_weights:{input_gpt_sovits_gpt_model_path.value} 加载失败,请查看双方日志排查问题"
  399. logger.error(content)
  400. return False
  401. return True
  402. except Exception as e:
  403. logger.error(traceback.format_exc())
  404. logger.error(f'gpt_sovits未知错误: {e}')
  405. return False
  406. async def set_sovits_weights():
  407. try:
  408. API_URL = urljoin(input_gpt_sovits_api_ip_port.value, '/set_sovits_weights?weights_path=' + input_gpt_sovits_sovits_model_path.value)
  409. resp_json = await common.send_async_request(API_URL, "GET", None, resp_data_type="json")
  410. if resp_json is None:
  411. content = f"sovits_weights:{input_gpt_sovits_sovits_model_path.value} 加载失败,请查看双方日志排查问题"
  412. logger.error(content)
  413. return False
  414. else:
  415. if resp_json["message"] == "success":
  416. content = f"sovits_weights:{input_gpt_sovits_sovits_model_path.value} 加载成功"
  417. logger.info(content)
  418. else:
  419. content = f"sovits_weights:{input_gpt_sovits_sovits_model_path.value} 加载失败,请查看双方日志排查问题"
  420. logger.error(content)
  421. return False
  422. return True
  423. except Exception as e:
  424. logger.error(traceback.format_exc())
  425. logger.error(f'sovits_weights未知错误: {e}')
  426. return False
  427. if await set_gpt_weights() and await set_sovits_weights():
  428. content = "gpt_sovits模型加载成功"
  429. logger.info(content)
  430. ui.notify(position="top", type="positive", message=content)
  431. else:
  432. content = "gpt_sovits模型加载失败,请查看双方日志排查问题"
  433. logger.error(content)
  434. ui.notify(position="top", type="negative", message=content)
  435. else:
  436. API_URL = urljoin(input_gpt_sovits_api_ip_port.value, '/set_model')
  437. data_json = {
  438. "gpt_model_path": input_gpt_sovits_gpt_model_path.value,
  439. "sovits_model_path": input_gpt_sovits_sovits_model_path.value
  440. }
  441. resp_data = await common.send_async_request(API_URL, "POST", data_json, resp_data_type="content")
  442. if resp_data is None:
  443. content = "gpt_sovits加载模型失败,请查看双方日志排查问题"
  444. logger.error(content)
  445. ui.notify(position="top", type="negative", message=content)
  446. else:
  447. content = "gpt_sovits加载模型成功"
  448. logger.info(content)
  449. ui.notify(position="top", type="positive", message=content)
  450. except Exception as e:
  451. logger.error(traceback.format_exc())
  452. logger.error(f'gpt_sovits未知错误: {e}')
  453. ui.notify(position="top", type="negative", message=f'gpt_sovits未知错误: {e}')
  454. # 页面滑到顶部
  455. def scroll_to_top():
  456. # 这段JavaScript代码将页面滚动到顶部
  457. ui.run_javascript("window.scrollTo(0, 0);")
  458. # 显示聊天数据的滚动框
  459. scroll_area_chat_box = None
  460. # 处理数据 显示聊天记录
  461. def data_handle_show_chat_log(data_json):
  462. global scroll_area_chat_box_chat_message_num
  463. if data_json["type"] == "llm":
  464. if data_json["data"]["content_type"] == "question":
  465. name = data_json["data"]['username']
  466. if 'user_face' in data_json["data"]:
  467. # 由于直接请求b站头像返回403 所以暂时还是用默认头像
  468. # avatar = data_json["data"]['user_face']
  469. avatar = 'https://robohash.org/ui'
  470. else:
  471. avatar = 'https://robohash.org/ui'
  472. else:
  473. name = data_json["data"]['type']
  474. avatar = "http://127.0.0.1:8081/favicon.ico"
  475. with scroll_area_chat_box:
  476. ui.chat_message(data_json["data"]["content"],
  477. name=name,
  478. stamp=data_json["data"]["timestamp"],
  479. avatar=avatar
  480. )
  481. scroll_area_chat_box_chat_message_num += 1
  482. if scroll_area_chat_box_chat_message_num > scroll_area_chat_box_chat_message_max_num:
  483. scroll_area_chat_box.remove(0)
  484. scroll_area_chat_box.scroll_to(percent=1, duration=0.2)
  485. """
  486. /@@@@@@@@ @@@@@@@@@@@@@@@]. =@@@@@@@
  487. =@@@@@@@@@^ @@@@@@@@@@@@@@@@@@` =@@@@@@@
  488. ,@@@@@@@@@@@` @@@@@@@@@@@@@@@@@@@^ =@@@@@@@
  489. .@@@@@@\@@@@@@. @@@@@@@^ .\@@@@@@\ =@@@@@@@
  490. /@@@@@/ \@@@@@\ @@@@@@@^ =@@@@@@@ =@@@@@@@
  491. =@@@@@@. .@@@@@@^ @@@@@@@\]]]@@@@@@@@^ =@@@@@@@
  492. ,@@@@@@^ =@@@@@@` @@@@@@@@@@@@@@@@@@/ =@@@@@@@
  493. .@@@@@@@@@@@@@@@@@@@. @@@@@@@@@@@@@@@@/` =@@@@@@@
  494. /@@@@@@@@@@@@@@@@@@@\ @@@@@@@^ =@@@@@@@
  495. =@@@@@@@@@@@@@@@@@@@@@^ @@@@@@@^ =@@@@@@@
  496. ,@@@@@@@. ,@@@@@@@` @@@@@@@^ =@@@@@@@
  497. @@@@@@@^ =@@@@@@@. @@@@@@@^ =@@@@@@@
  498. """
  499. from starlette.requests import Request
  500. from utils.models import SendMessage, CommonResult, SysCmdMessage, SetConfigMessage
  501. """
  502. 配置config
  503. config_path (str): 配置文件路径
  504. data (dict): 传入的json
  505. return:
  506. {"code": 200, "message": "成功"}
  507. """
  508. @app.post('/set_config')
  509. async def set_config(msg: SetConfigMessage):
  510. global config
  511. try:
  512. data_json = msg.dict()
  513. logger.info(f'set_config接口 收到数据:{data_json}')
  514. config_data = None
  515. try:
  516. with open(data_json["config_path"], 'r', encoding="utf-8") as config_file:
  517. config_data = json.load(config_file)
  518. except Exception as e:
  519. logger.error(f"无法读取配置文件!\n{e}")
  520. return CommonResult(code=-1, message=f"无法读取配置文件!{e}")
  521. # 合并字典
  522. config_data.update(data_json["data"])
  523. # 写入配置到配置文件
  524. try:
  525. with open(data_json["config_path"], 'w', encoding="utf-8") as config_file:
  526. json.dump(config_data, config_file, indent=2, ensure_ascii=False)
  527. config_file.flush() # 刷新缓冲区,确保写入立即生效
  528. logger.info("配置数据已成功写入文件!")
  529. return CommonResult(code=200, message="配置数据已成功写入文件!")
  530. except Exception as e:
  531. logger.error(f"无法写入配置文件!\n{str(e)}")
  532. return CommonResult(code=-1, message=f"无法写入配置文件!{e}")
  533. except Exception as e:
  534. logger.error(traceback.format_exc())
  535. return CommonResult(code=-1, message=f"{data_json['type']}执行失败!{e}")
  536. """
  537. 系统命令
  538. type 命令类型(run/stop/restart/factory)
  539. data 传入的json
  540. data_json = {
  541. "type": "命令名",
  542. "data": {
  543. "key": "value"
  544. }
  545. }
  546. return:
  547. {"code": 200, "message": "成功"}
  548. {"code": -1, "message": "失败"}
  549. """
  550. @app.post('/sys_cmd')
  551. async def sys_cmd(msg: SysCmdMessage):
  552. try:
  553. data_json = msg.dict()
  554. logger.info(f'sys_cmd接口 收到数据:{data_json}')
  555. logger.info(f"开始执行 {data_json['type']}命令...")
  556. resp_json = {}
  557. if data_json['type'] == 'run':
  558. """
  559. {
  560. "type": "run",
  561. "data": {
  562. "config_path": "config.json"
  563. }
  564. }
  565. """
  566. # 运行
  567. resp_json = run_external_program(data_json['data']['config_path'], type="api")
  568. elif data_json['type'] =='stop':
  569. """
  570. {
  571. "type": "stop",
  572. "data": {
  573. "config_path": "config.json"
  574. }
  575. }
  576. """
  577. # 停止
  578. resp_json = stop_external_program(type="api")
  579. elif data_json['type'] =='restart':
  580. """
  581. {
  582. "type": "restart",
  583. "api_type": "webui",
  584. "data": {
  585. "config_path": "config.json"
  586. }
  587. }
  588. """
  589. # 重启
  590. resp_json = restart_application(type=data_json['api_type'])
  591. elif data_json['type'] =='factory':
  592. """
  593. {
  594. "type": "factory",
  595. "api_type": "webui",
  596. "data": {
  597. "src_path": "config.json.bak",
  598. "dst_path": "config.json"
  599. }
  600. }
  601. """
  602. # 恢复出厂
  603. resp_json = factory(data_json['data']['src_path'], data_json['data']['dst_path'], type="api")
  604. return resp_json
  605. except Exception as e:
  606. logger.error(traceback.format_exc())
  607. return CommonResult(code=-1, message=f"{data_json['type']}执行失败!{e}")
  608. """
  609. 发送数据
  610. type 数据类型(comment/gift/entrance/reread/tuning/...)
  611. key 根据数据类型自行适配
  612. data_json = {
  613. "type": "数据类型",
  614. "key": "value"
  615. }
  616. return:
  617. {"code": 200, "message": "成功"}
  618. {"code": -1, "message": "失败"}
  619. """
  620. @app.post('/send')
  621. async def send(msg: SendMessage):
  622. global config
  623. try:
  624. data_json = msg.dict()
  625. logger.info(f'WEBUI API send接口收到数据:{data_json}')
  626. main_api_ip = "127.0.0.1" if config.get("api_ip") == "0.0.0.0" else config.get("api_ip")
  627. resp_json = await common.send_async_request(f'http://{main_api_ip}:{config.get("api_port")}/send', "POST", data_json)
  628. return resp_json
  629. except Exception as e:
  630. logger.error(traceback.format_exc())
  631. return CommonResult(code=-1, message=f"发送数据失败!{e}")
  632. """
  633. 数据回调
  634. data 传入的json
  635. data_json = {
  636. "type": "数据类型(llm)",
  637. "data": {
  638. "type": "LLM类型",
  639. "username": "用户名",
  640. "content_type": "内容的类型(question/answer)",
  641. "content": "回复内容",
  642. "timestamp": "时间戳"
  643. }
  644. }
  645. return:
  646. {"code": 200, "message": "成功"}
  647. {"code": -1, "message": "失败"}
  648. """
  649. @app.post('/callback')
  650. async def callback(request: Request):
  651. try:
  652. data_json = await request.json()
  653. logger.info(f'WEBUI API callback接口收到数据:{data_json}')
  654. data_handle_show_chat_log(data_json)
  655. return {"code": 200, "message": "成功"}
  656. except Exception as e:
  657. logger.error(traceback.format_exc())
  658. return CommonResult(code=-1, message=f"失败!{e}")
  659. """
  660. TTS合成,获取合成的音频文件路径
  661. data 传入的json
  662. 例如:
  663. data_json = {
  664. "type": "reread",
  665. "tts_type": "gpt_sovits",
  666. "data": {
  667. "type": "api",
  668. "ws_ip_port": "ws://localhost:9872/queue/join",
  669. "api_ip_port": "http://127.0.0.1:9880",
  670. "ref_audio_path": "F:\\GPT-SoVITS\\raws\\ikaros\\21.wav",
  671. "prompt_text": "マスター、どうりょくろか、いいえ、なんでもありません",
  672. "prompt_language": "日文",
  673. "language": "自动识别",
  674. "cut": "凑四句一切",
  675. "gpt_model_path": "F:\\GPT-SoVITS\\GPT_weights\\ikaros-e15.ckpt",
  676. "sovits_model_path": "F:\\GPT-SoVITS\\SoVITS_weights\\ikaros_e8_s280.pth",
  677. "webtts": {
  678. "api_ip_port": "http://127.0.0.1:8080",
  679. "spk": "sanyueqi",
  680. "lang": "zh",
  681. "speed": "1.0",
  682. "emotion": "正常"
  683. }
  684. },
  685. "username": "主人",
  686. "content": "你好,这就是需要合成的文本内容"
  687. }
  688. return:
  689. {
  690. "code": 200,
  691. "message": "成功",
  692. "data": {
  693. "type": "reread",
  694. "tts_type": "gpt_sovits",
  695. "data": {
  696. "type": "api",
  697. "ws_ip_port": "ws://localhost:9872/queue/join",
  698. "api_ip_port": "http://127.0.0.1:9880",
  699. "ref_audio_path": "F:\\\\GPT-SoVITS\\\\raws\\\\ikaros\\\\21.wav",
  700. "prompt_text": "マスター、どうりょくろか、いいえ、なんでもありません",
  701. "prompt_language": "日文",
  702. "language": "自动识别",
  703. "cut": "凑四句一切",
  704. "gpt_model_path": "F:\\GPT-SoVITS\\GPT_weights\\ikaros-e15.ckpt",
  705. "sovits_model_path": "F:\\GPT-SoVITS\\SoVITS_weights\\ikaros_e8_s280.pth",
  706. "webtts": {
  707. "api_ip_port": "http://127.0.0.1:8080",
  708. "spk": "sanyueqi",
  709. "lang": "zh",
  710. "speed": "1.0",
  711. "emotion": "正常"
  712. }
  713. },
  714. "username": "主人",
  715. "content": "你好,这就是需要合成的文本内容",
  716. "result": {
  717. "code": 200,
  718. "msg": "合成成功",
  719. "audio_path": "E:\\GitHub_pro\\AI-Vtuber\\out\\gpt_sovits_4.wav"
  720. }
  721. }
  722. }
  723. {"code": -1, "message": "失败"}
  724. """
  725. @app.post('/tts')
  726. async def tts(request: Request):
  727. try:
  728. data_json = await request.json()
  729. logger.info(f'WEBUI API tts接口收到数据:{data_json}')
  730. resp_json = await audio.tts_handle(data_json)
  731. return {"code": 200, "message": "成功", "data": resp_json}
  732. except Exception as e:
  733. logger.error(traceback.format_exc())
  734. return CommonResult(code=-1, message=f"失败!{e}")
  735. """
  736. LLM推理,获取推理结果
  737. data 传入的json
  738. 例如:type就是聊天类型实际对应的值
  739. data_json = {
  740. "type": "chatgpt",
  741. "username": "用户名",
  742. "content": "你好"
  743. }
  744. return:
  745. {
  746. "code": 200,
  747. "message": "成功",
  748. "data": {
  749. "content": "你好,这是LLM回复的内容"
  750. }
  751. }
  752. {"code": -1, "message": "失败"}
  753. """
  754. @app.post('/llm')
  755. async def llm(request: Request):
  756. try:
  757. data_json = await request.json()
  758. logger.info(f'WEBUI API llm接口 收到数据:{data_json}')
  759. main_api_ip = "127.0.0.1" if config.get("api_ip") == "0.0.0.0" else config.get("api_ip")
  760. resp_json = await common.send_async_request(f'http://{main_api_ip}:{config.get("api_port")}/llm', "POST", data_json, "json", timeout=60)
  761. if resp_json:
  762. return resp_json
  763. return CommonResult(code=-1, message="失败!")
  764. except Exception as e:
  765. logger.error(traceback.format_exc())
  766. return CommonResult(code=-1, message=f"失败!{e}")
  767. # fish speech 获取说话人数据
  768. async def fish_speech_web_get_ref_data(speaker):
  769. if speaker == "":
  770. logger.info("说话人不能为空喵~")
  771. ui.notify(position="top", type="warning", message="说话人不能为空喵~")
  772. return
  773. from utils.audio_handle.my_tts import MY_TTS
  774. my_tts = MY_TTS(config_path)
  775. data_json = await my_tts.fish_speech_web_get_ref_data(speaker)
  776. if data_json is None:
  777. ui.notify(position="top", type="negative", message="获取数据失败,请查看日志定位问题")
  778. return
  779. input_fish_speech_web_ref_audio_path.value = data_json["ref_audio_path"]
  780. input_fish_speech_web_ref_text.value = data_json["ref_text"]
  781. ui.notify(position="top", type="positive", message="获取数据成功,已自动填入输入框")
  782. """
  783. ./@\]
  784. ,@@@@\* \@@^ ,]]]
  785. [[[* /@@]@@@@@/[[\@@@@/
  786. ]]@@@@@@\ /@@^ @@@^]]`[[
  787. ]]@@@@@@@[[* ,[` /@@\@@@@@@@@@@@@@@^
  788. [[[[[` @@@/ \@@@@[[[\@@^ =@@/
  789. .\@@\* *@@@` [\@@@@@@\`
  790. ,@@\=@@@ ,]@@@/` ,\@@@@*
  791. ,@@@@` ,[[[[` =@@@ ]]/O
  792. /@@@@@` ]]]@@@@@@@@@/[[[[[`
  793. ,@@@@[ \@@@\` ./@@@@@@@]
  794. ,]/@@@@/` \@@@@@\]] ,@@@/,@@^ \@@@\]
  795. ,@@@@@@@@/[* ,/@@/* /@@^ [@@@@@@@\*
  796. ,@@^
  797. """
  798. # 文案页-增加
  799. def copywriting_add():
  800. data_len = len(copywriting_config_var)
  801. tmp_config = {
  802. "file_path": f"data/copywriting{int(data_len / 5) + 1}/",
  803. "audio_path": f"out/copywriting{int(data_len / 5) + 1}/",
  804. "continuous_play_num": 2,
  805. "max_play_time": 10.0,
  806. "play_list": []
  807. }
  808. with copywriting_config_card.style(card_css):
  809. with ui.row():
  810. copywriting_config_var[str(data_len)] = ui.input(label=f"文案存储路径#{int(data_len / 5) + 1}", value=tmp_config["file_path"], placeholder='文案文件存储路径。不建议更改。').style("width:200px;")
  811. copywriting_config_var[str(data_len + 1)] = ui.input(label=f"音频存储路径#{int(data_len / 5) + 1}", value=tmp_config["audio_path"], placeholder='文案音频文件存储路径。不建议更改。').style("width:200px;")
  812. copywriting_config_var[str(data_len + 2)] = ui.input(label=f"连续播放数#{int(data_len / 5) + 1}", value=tmp_config["continuous_play_num"], placeholder='文案播放列表中连续播放的音频文件个数,如果超过了这个个数就会切换下一个文案列表').style("width:200px;")
  813. copywriting_config_var[str(data_len + 3)] = ui.input(label=f"连续播放时间#{int(data_len / 5) + 1}", value=tmp_config["max_play_time"], placeholder='文案播放列表中连续播放音频的时长,如果超过了这个时长就会切换下一个文案列表').style("width:200px;")
  814. copywriting_config_var[str(data_len + 4)] = ui.textarea(label=f"播放列表#{int(data_len / 5) + 1}", value=textarea_data_change(tmp_config["play_list"]), placeholder='此处填写需要播放的音频文件全名,填写完毕后点击 保存配置。文件全名从音频列表中复制,换行分隔,请勿随意填写').style("width:500px;")
  815. # 文案页-删除
  816. def copywriting_del(index):
  817. try:
  818. copywriting_config_card.remove(int(index) - 1)
  819. # 删除操作
  820. keys_to_delete = [str(5 * (int(index) - 1) + i) for i in range(5)]
  821. for key in keys_to_delete:
  822. if key in copywriting_config_var:
  823. del copywriting_config_var[key]
  824. # 重新编号剩余的键
  825. updates = {}
  826. for key in sorted(copywriting_config_var.keys(), key=int):
  827. new_key = str(int(key) - 5 if int(key) > int(keys_to_delete[-1]) else key)
  828. updates[new_key] = copywriting_config_var[key]
  829. # 应用更新
  830. copywriting_config_var.clear()
  831. copywriting_config_var.update(updates)
  832. except Exception as e:
  833. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  834. logger.error(traceback.format_exc())
  835. # 文案页-加载文本
  836. def copywriting_text_load():
  837. copywriting_text_path = input_copywriting_text_path.value
  838. if "" == copywriting_text_path:
  839. logger.warning(f"请输入 文案文本路径喵~")
  840. ui.notify(position="top", type="warning", message="请输入 文案文本路径喵~")
  841. return
  842. # 传入完整文件路径 绝对或相对
  843. logger.info(f"准备加载 文件:[{copywriting_text_path}]")
  844. new_file_path = os.path.join(copywriting_text_path)
  845. content = common.read_file_return_content(new_file_path)
  846. if content is None:
  847. logger.error(f"读取失败!请检测配置、文件路径、文件名")
  848. ui.notify(position="top", type="negative", message="读取失败!请检测配置、文件路径、文件名")
  849. return
  850. # 数据写入文本输入框中
  851. textarea_copywriting_text.value = content
  852. logger.info(f"成功加载文案:{copywriting_text_path}")
  853. ui.notify(position="top", type="positive", message=f"成功加载文案:{copywriting_text_path}")
  854. # 文案页-保存文案
  855. def copywriting_save_text():
  856. content = textarea_copywriting_text.value
  857. copywriting_text_path = input_copywriting_text_path.value
  858. if "" == copywriting_text_path:
  859. logger.warning(f"请输入 文案文本路径喵~")
  860. ui.notify(position="top", type="warning", message="请输入 文案文本路径喵~")
  861. return
  862. new_file_path = os.path.join(copywriting_text_path)
  863. if common.write_content_to_file(new_file_path, content):
  864. ui.notify(position="top", type="positive", message=f"保存成功~")
  865. else:
  866. ui.notify(position="top", type="negative", message=f"保存失败!请查看日志排查问题")
  867. # 文案页-合成音频
  868. async def copywriting_audio_synthesis():
  869. ui.notify(position="top", type="warning", message="文案音频合成中,将会阻塞其他任务运行,请勿做其他操作,查看日志情况,耐心等待")
  870. logger.warning("文案音频合成中,将会阻塞其他任务运行,请勿做其他操作,查看日志情况,耐心等待")
  871. copywriting_text_path = input_copywriting_text_path.value
  872. copywriting_audio_save_path = input_copywriting_audio_save_path.value
  873. audio_synthesis_type = select_copywriting_audio_synthesis_type.value
  874. file_path = await audio.copywriting_synthesis_audio(copywriting_text_path, copywriting_audio_save_path, audio_synthesis_type)
  875. if file_path:
  876. ui.notify(position="top", type="positive", message=f"文案音频合成成功,存储于:{file_path}")
  877. else:
  878. ui.notify(position="top", type="negative", message=f"文案音频合成失败!请查看日志排查问题")
  879. return
  880. def clear_copywriting_audio_card(file_path):
  881. copywriting_audio_card.clear()
  882. if common.del_file(file_path):
  883. ui.notify(position="top", type="positive", message=f"删除文件成功:{file_path}")
  884. else:
  885. ui.notify(position="top", type="negative", message=f"删除文件失败:{file_path}")
  886. # 清空card
  887. copywriting_audio_card.clear()
  888. tmp_label = ui.label(f"文案音频合成成功,存储于:{file_path}")
  889. tmp_label.move(copywriting_audio_card)
  890. audio_copywriting = ui.audio(src=file_path)
  891. audio_copywriting.move(copywriting_audio_card)
  892. button_copywriting_audio_del = ui.button('删除音频', on_click=lambda: clear_copywriting_audio_card(file_path), color=button_internal_color).style(button_internal_css)
  893. button_copywriting_audio_del.move(copywriting_audio_card)
  894. # 文案页-循环播放
  895. def copywriting_loop_play():
  896. if running_flag != 1:
  897. ui.notify(position="top", type="warning", message=f"请先点击“一键运行”,然后再进行播放")
  898. return
  899. logger.info("开始循环播放文案~")
  900. ui.notify(position="top", type="positive", message="开始循环播放文案~")
  901. audio.unpause_copywriting_play()
  902. # 文案页-暂停播放
  903. def copywriting_pause_play():
  904. if running_flag != 1:
  905. ui.notify(position="top", type="warning", message=f"请先点击“一键运行”,然后再进行暂停")
  906. return
  907. audio.pause_copywriting_play()
  908. logger.info("暂停文案完毕~")
  909. ui.notify(position="top", type="positive", message="暂停文案完毕~")
  910. """
  911. 定时任务
  912. """
  913. # -增加
  914. def schedule_add():
  915. data_len = len(schedule_var)
  916. tmp_config = {
  917. "enable": False,
  918. "time_min": 60,
  919. "time_max": 120,
  920. "copy": []
  921. }
  922. with schedule_config_card.style(card_css):
  923. with ui.row():
  924. schedule_var[str(data_len)] = ui.switch(text=f"启用任务#{int(data_len / 4) + 1}", value=tmp_config["enable"]).style(switch_internal_css)
  925. schedule_var[str(data_len + 1)] = ui.input(label=f"最小循环周期#{int(data_len / 4) + 1}", value=tmp_config["time_min"], placeholder='定时任务循环的周期最小时长(秒),即每间隔这个周期就会执行一次').style("width:100px;")
  926. schedule_var[str(data_len + 2)] = ui.input(label=f"最大循环周期#{int(data_len / 4) + 1}", value=tmp_config["time_max"], placeholder='定时任务循环的周期最大时长(秒),即每间隔这个周期就会执行一次').style("width:100px;")
  927. schedule_var[str(data_len + 3)] = ui.textarea(label=f"文案列表#{int(data_len / 4) + 1}", value=textarea_data_change(tmp_config["copy"]), placeholder='存放文案的列表,通过空格或换行分割,通过{变量}来替换关键数据,可修改源码自定义功能').style("width:500px;")
  928. # -删除
  929. def schedule_del(index):
  930. try:
  931. schedule_config_card.remove(int(index) - 1)
  932. # 删除操作
  933. keys_to_delete = [str(4 * (int(index) - 1) + i) for i in range(4)]
  934. for key in keys_to_delete:
  935. if key in schedule_var:
  936. del schedule_var[key]
  937. # 重新编号剩余的键
  938. updates = {}
  939. for key in sorted(schedule_var.keys(), key=int):
  940. new_key = str(int(key) - 4 if int(key) > int(keys_to_delete[-1]) else key)
  941. updates[new_key] = schedule_var[key]
  942. # 应用更新
  943. schedule_var.clear()
  944. schedule_var.update(updates)
  945. except Exception as e:
  946. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  947. logger.error(traceback.format_exc())
  948. """
  949. 动态文案
  950. """
  951. # 动态文案-增加
  952. def trends_copywriting_add():
  953. data_len = len(trends_copywriting_copywriting_var)
  954. tmp_config = {
  955. "folder_path": "",
  956. "prompt_change_enable": False,
  957. "prompt_change_content": ""
  958. }
  959. with trends_copywriting_config_card.style(card_css):
  960. with ui.row():
  961. trends_copywriting_copywriting_var[str(data_len)] = ui.input(label=f"文案路径#{int(data_len / 3) + 1}", value=tmp_config["folder_path"], placeholder='文案文件存储的文件夹路径').style("width:200px;")
  962. trends_copywriting_copywriting_var[str(data_len + 1)] = ui.switch(text=f"提示词转换#{int(data_len / 3) + 1}", value=tmp_config["prompt_change_enable"])
  963. trends_copywriting_copywriting_var[str(data_len + 2)] = ui.input(label=f"提示词转换内容#{int(data_len / 3) + 1}", value=tmp_config["prompt_change_content"], placeholder='使用此提示词内容对文案内容进行转换后再进行合成,使用的LLM为聊天类型配置').style("width:500px;")
  964. # 动态文案-删除
  965. def trends_copywriting_del(index):
  966. try:
  967. trends_copywriting_config_card.remove(int(index) - 1)
  968. # 删除操作
  969. keys_to_delete = [str(3 * (int(index) - 1) + i) for i in range(3)]
  970. for key in keys_to_delete:
  971. if key in trends_copywriting_copywriting_var:
  972. del trends_copywriting_copywriting_var[key]
  973. # 重新编号剩余的键
  974. updates = {}
  975. for key in sorted(trends_copywriting_copywriting_var.keys(), key=int):
  976. new_key = str(int(key) - 3 if int(key) > int(keys_to_delete[-1]) else key)
  977. updates[new_key] = trends_copywriting_copywriting_var[key]
  978. # 应用更新
  979. trends_copywriting_copywriting_var.clear()
  980. trends_copywriting_copywriting_var.update(updates)
  981. except Exception as e:
  982. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  983. logger.error(traceback.format_exc())
  984. """
  985. 联动程序
  986. """
  987. # 联动程序-增加
  988. def coordination_program_add():
  989. data_len = len(coordination_program_var)
  990. tmp_config = {
  991. "enable": True,
  992. "name": "",
  993. "executable": "",
  994. "parameters": []
  995. }
  996. with coordination_program_config_card.style(card_css):
  997. with ui.row():
  998. coordination_program_var[str(data_len)] = ui.switch(f'启用#{int(data_len / 4) + 1}', value=tmp_config["enable"]).style(switch_internal_css)
  999. coordination_program_var[str(data_len + 1)] = ui.input(label=f"程序名#{int(data_len / 4) + 1}", value=tmp_config["name"], placeholder='给你的程序取个名字,别整特殊符号!').style("width:200px;")
  1000. coordination_program_var[str(data_len + 2)] = ui.input(label=f"可执行程序#{int(data_len / 4) + 1}", value=tmp_config["executable"], placeholder='可执行程序的路径,最好是绝对路径,如python的程序').style("width:400px;")
  1001. coordination_program_var[str(data_len + 3)] = ui.textarea(label=f'参数#{int(data_len / 4) + 1}', value=textarea_data_change(tmp_config["parameters"]), placeholder='参数,可以传入多个参数,换行分隔。如启动的程序的路径,命令携带的传参等').style("width:500px;")
  1002. # 联动程序-删除
  1003. def coordination_program_del(index):
  1004. try:
  1005. coordination_program_config_card.remove(int(index) - 1)
  1006. # 删除操作
  1007. keys_to_delete = [str(4 * (int(index) - 1) + i) for i in range(4)]
  1008. for key in keys_to_delete:
  1009. if key in coordination_program_var:
  1010. del coordination_program_var[key]
  1011. # 重新编号剩余的键
  1012. updates = {}
  1013. for key in sorted(coordination_program_var.keys(), key=int):
  1014. new_key = str(int(key) - 4 if int(key) > int(keys_to_delete[-1]) else key)
  1015. updates[new_key] = coordination_program_var[key]
  1016. # 应用更新
  1017. coordination_program_var.clear()
  1018. coordination_program_var.update(updates)
  1019. except Exception as e:
  1020. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  1021. logger.error(traceback.format_exc())
  1022. """
  1023. 按键/文案映射
  1024. """
  1025. def key_mapping_add():
  1026. data_len = len(key_mapping_config_var)
  1027. tmp_config = {
  1028. "keywords": [],
  1029. "gift": [],
  1030. "keys": [],
  1031. "similarity": 1,
  1032. "copywriting": [],
  1033. "local_audio": [],
  1034. "img_path": []
  1035. }
  1036. with key_mapping_config_card.style(card_css):
  1037. with ui.row():
  1038. num = int(data_len / 9) + 1
  1039. key_mapping_config_var[str(data_len)] = ui.textarea(label=f"关键词#{num}", value=textarea_data_change(tmp_config["keywords"]), placeholder='此处输入触发的关键词,多个请以换行分隔').style("width:100px;")
  1040. key_mapping_config_var[str(data_len + 1)] = ui.textarea(label=f"礼物#{num}", value=textarea_data_change(tmp_config["gift"]), placeholder='此处输入触发的礼物名,多个请以换行分隔').style("width:100px;")
  1041. key_mapping_config_var[str(data_len + 2)] = ui.textarea(label=f"按键#{num}", value=textarea_data_change(tmp_config["keys"]), placeholder='此处输入你要映射的按键,多个按键请以换行分隔(按键名参考pyautogui规则)').style("width:100px;")
  1042. key_mapping_config_var[str(data_len + 3)] = ui.input(label=f"相似度#{num}", value=tmp_config["similarity"], placeholder='关键词与用户输入的相似度,默认1即100%').style("width:50px;")
  1043. key_mapping_config_var[str(data_len + 4)] = ui.textarea(label=f"文案#{num}", value=textarea_data_change(tmp_config["copywriting"]), placeholder='此处输入触发后合成的文案内容,多个请以换行分隔').style("width:300px;")
  1044. key_mapping_config_var[str(data_len + 5)] = ui.textarea(label=f"文案#{num}", value=textarea_data_change(tmp_config["copywriting"]), placeholder='此处输入触发后合成的文案内容,多个请以换行分隔').style("width:300px;")
  1045. key_mapping_config_var[str(data_len + 6)] = ui.input(label=f"串口名#{num}", value=tmp_config["serial_name"], placeholder='例如:COM1').style("width:100px;").tooltip('串口页配置的串口名,例如:COM1')
  1046. key_mapping_config_var[str(data_len + 7)] = ui.textarea(label=f"串口发送内容#{num}", value=textarea_data_change(tmp_config["serial_send_data"]), placeholder='多个请以换行分隔,ASCII例如:open led\nHEX例如(2个字符的十六进制字符):313233').style("width:300px;").tooltip('此处输入发送到串口的数据内容,数据类型根据串口页设置决定,多个请以换行分隔')
  1047. key_mapping_config_var[str(data_len + 8)] = ui.textarea(label=f"串口发送内容#{num}", value=textarea_data_change(tmp_config["serial_send_data"]), placeholder='多个请以换行分隔,ASCII例如:open led\nHEX例如(2个字符的十六进制字符):313233').style("width:300px;").tooltip('此处输入发送到串口的数据内容,数据类型根据串口页设置决定,多个请以换行分隔')
  1048. def key_mapping_del(index):
  1049. try:
  1050. num = 9
  1051. key_mapping_config_card.remove(int(index) - 1)
  1052. # 删除操作
  1053. keys_to_delete = [str(num * (int(index) - 1) + i) for i in range(num)]
  1054. for key in keys_to_delete:
  1055. if key in key_mapping_config_var:
  1056. del key_mapping_config_var[key]
  1057. # 重新编号剩余的键
  1058. updates = {}
  1059. for key in sorted(key_mapping_config_var.keys(), key=int):
  1060. new_key = str(int(key) - num if int(key) > int(keys_to_delete[-1]) else key)
  1061. updates[new_key] = key_mapping_config_var[key]
  1062. # 应用更新
  1063. key_mapping_config_var.clear()
  1064. key_mapping_config_var.update(updates)
  1065. except Exception as e:
  1066. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  1067. logger.error(traceback.format_exc())
  1068. """
  1069. 自定义命令
  1070. """
  1071. # 自定义命令-增加
  1072. def custom_cmd_add():
  1073. data_len = len(custom_cmd_config_var)
  1074. tmp_config = {
  1075. "keywords": [],
  1076. "similarity": 1,
  1077. "api_url": "",
  1078. "api_type": "",
  1079. "resp_data_type": "",
  1080. "data_analysis": "",
  1081. "resp_template": ""
  1082. }
  1083. with custom_cmd_config_card.style(card_css):
  1084. with ui.row():
  1085. custom_cmd_config_var[str(data_len)] = ui.textarea(label=f"关键词#{int(data_len / 7) + 1}", value=textarea_data_change(tmp_config["keywords"]), placeholder='此处输入触发的关键词,多个请以换行分隔').style("width:200px;")
  1086. custom_cmd_config_var[str(data_len + 1)] = ui.input(label=f"相似度#{int(data_len / 7) + 1}", value=tmp_config["similarity"], placeholder='关键词与用户输入的相似度,默认1即100%').style("width:100px;")
  1087. custom_cmd_config_var[str(data_len + 2)] = ui.textarea(label=f"API URL#{int(data_len / 7) + 1}", value=tmp_config["api_url"], placeholder='发送HTTP请求的API链接', validation={'请输入正确格式的URL': lambda value: common.is_url_check(value),}).style("width:300px;")
  1088. custom_cmd_config_var[str(data_len + 3)] = ui.select(label=f"API类型#{int(data_len / 7) + 1}", value=tmp_config["api_type"], options={"GET": "GET"}).style("width:100px;")
  1089. custom_cmd_config_var[str(data_len + 4)] = ui.select(label=f"请求返回数据类型#{int(data_len / 7) + 1}", value=tmp_config["resp_data_type"], options={"json": "json", "content": "content"}).style("width:150px;")
  1090. custom_cmd_config_var[str(data_len + 5)] = ui.textarea(label=f"数据解析(eval执行)#{int(data_len / 7) + 1}", value=tmp_config["data_analysis"], placeholder='数据解析,请不要随意修改resp变量,会被用于最后返回数据内容的解析').style("width:200px;")
  1091. custom_cmd_config_var[str(data_len + 6)] = ui.textarea(label=f"返回内容模板#{int(data_len / 7) + 1}", value=tmp_config["resp_template"], placeholder='请不要随意删除data变量,支持动态变量,最终会合并成完成内容进行音频合成').style("width:300px;")
  1092. # 自定义命令-删除
  1093. def custom_cmd_del(index):
  1094. try:
  1095. custom_cmd_config_card.remove(int(index) - 1)
  1096. # 删除操作
  1097. keys_to_delete = [str(7 * (int(index) - 1) + i) for i in range(7)]
  1098. for key in keys_to_delete:
  1099. if key in custom_cmd_config_var:
  1100. del custom_cmd_config_var[key]
  1101. # 重新编号剩余的键
  1102. updates = {}
  1103. for key in sorted(custom_cmd_config_var.keys(), key=int):
  1104. new_key = str(int(key) - 7 if int(key) > int(keys_to_delete[-1]) else key)
  1105. updates[new_key] = custom_cmd_config_var[key]
  1106. # 应用更新
  1107. custom_cmd_config_var.clear()
  1108. custom_cmd_config_var.update(updates)
  1109. except Exception as e:
  1110. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  1111. logger.error(traceback.format_exc())
  1112. """
  1113. 串口
  1114. """
  1115. def serial_config_add():
  1116. data_len = len(serial_config_var)
  1117. tmp_config = {
  1118. "serial_name": "COM1",
  1119. "baudrate": "115200",
  1120. "serial_data_type": "ASCII"
  1121. }
  1122. with serial_config_card.style(card_css):
  1123. with ui.row():
  1124. serial_config_var[str(data_len)] = ui.select(label=f"串口名#{int(data_len / 8) + 1}", value=tmp_config["serial_name"], options={f'{tmp_config["serial_name"]}': f'{tmp_config["serial_name"]}'}).style("width:200px;").tooltip('文案文件存储路径。不建议更改。')
  1125. serial_config_var[str(data_len + 1)] = ui.select(
  1126. label=f"波特率#{int(data_len / 8) + 1}",
  1127. value=tmp_config["baudrate"],
  1128. options={'9600': '9600', '19200': '19200', '38400': '38400', '115200': '115200'}
  1129. ).style("width:200px;").tooltip('波特率')
  1130. serial_config_var[str(data_len + 2)] = ui.button('刷新串口', on_click=lambda: refresh_serial(int(data_len / 8)))
  1131. serial_config_var[str(data_len + 3)] = ui.button('打开串口', on_click=lambda: connect_serial(int(data_len / 8)))
  1132. serial_config_var[str(data_len + 4)] = ui.button('关闭串口', on_click=lambda: disconnect_serial(int(data_len / 8)))
  1133. serial_config_var[str(data_len + 5)] = ui.select(label=f"发送数据类型#{int(data_len / 8) + 1}", value=tmp_config["serial_data_type"], options={'ASCII': 'ASCII', 'HEX': 'HEX'},).style("width:100px;").tooltip('发送的数据类型')
  1134. serial_config_var[str(data_len + 6)] = ui.input(label=f"发送数据#{int(data_len / 8) + 1}", value="", placeholder='填要发的内容,连接后,点 发送').style("width:200px;").tooltip('填要发的内容,连接后,点 发送')
  1135. serial_config_var[str(data_len + 7)] = ui.button('发送', on_click=lambda: send_data_to_serial(int(data_len / 8)))
  1136. def serial_config_del(index):
  1137. try:
  1138. serial_config_card.remove(int(index) - 1)
  1139. # 删除操作
  1140. keys_to_delete = [str(8 * (int(index) - 1) + i) for i in range(8)]
  1141. for key in keys_to_delete:
  1142. if key in serial_config_var:
  1143. del serial_config_var[key]
  1144. # 重新编号剩余的键
  1145. updates = {}
  1146. for key in sorted(serial_config_var.keys(), key=int):
  1147. new_key = str(int(key) - 8 if int(key) > int(keys_to_delete[-1]) else key)
  1148. updates[new_key] = serial_config_var[key]
  1149. # 应用更新
  1150. serial_config_var.clear()
  1151. serial_config_var.update(updates)
  1152. except Exception as e:
  1153. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  1154. logger.error(traceback.format_exc())
  1155. """
  1156. 添加本地路径到URL路径
  1157. """
  1158. # -增加
  1159. def webui_local_dir_to_endpoint_add():
  1160. data_len = len(webui_local_dir_to_endpoint_config_var)
  1161. tmp_config = {
  1162. "url_path": "",
  1163. "local_dir": "",
  1164. }
  1165. with webui_local_dir_to_endpoint_config_card.style(card_css):
  1166. with ui.row():
  1167. webui_local_dir_to_endpoint_config_var[str(data_len)] = ui.input(label=f"URL路径#{int(data_len / 2) + 1}", value=tmp_config["url_path"], placeholder='以斜杠("/")开始的字符串,它标识了应该为客户端提供文件的URL路径').style("width:300px;")
  1168. webui_local_dir_to_endpoint_config_var[str(data_len + 1)] = ui.input(label=f"本地文件夹路径#{int(data_len / 2) + 1}", value=tmp_config["local_dir"], placeholder='本地文件夹路径,建议相对路径,最好是项目内部的路径').style("width:300px;")
  1169. # -删除
  1170. def webui_local_dir_to_endpoint_del(index):
  1171. try:
  1172. webui_local_dir_to_endpoint_config_card.remove(int(index) - 1)
  1173. # 删除操作
  1174. keys_to_delete = [str(2 * (int(index) - 1) + i) for i in range(2)]
  1175. for key in keys_to_delete:
  1176. if key in webui_local_dir_to_endpoint_config_var:
  1177. del webui_local_dir_to_endpoint_config_var[key]
  1178. # 重新编号剩余的键
  1179. updates = {}
  1180. for key in sorted(webui_local_dir_to_endpoint_config_var.keys(), key=int):
  1181. new_key = str(int(key) - 2 if int(key) > int(keys_to_delete[-1]) else key)
  1182. updates[new_key] = webui_local_dir_to_endpoint_config_var[key]
  1183. # 应用更新
  1184. webui_local_dir_to_endpoint_config_var.clear()
  1185. webui_local_dir_to_endpoint_config_var.update(updates)
  1186. except Exception as e:
  1187. ui.notify(position="top", type="negative", message=f"错误,索引值配置有误:{e}")
  1188. logger.error(traceback.format_exc())
  1189. # 配置模板保存
  1190. def config_template_save(file_path: str):
  1191. try:
  1192. with open(config_path, 'r', encoding="utf-8") as config_file:
  1193. config_data = json.load(config_file)
  1194. config_data = webui_config_to_dict(config_data)
  1195. # 将JSON数据保存到文件中
  1196. with open(file_path, "w", encoding="utf-8") as file:
  1197. json.dump(config_data, file, indent=2, ensure_ascii=False)
  1198. file.flush() # 刷新缓冲区,确保写入立即生效
  1199. logger.info("配置模板保存成功!")
  1200. ui.notify(position="top", type="positive", message=f"配置模板保存成功!")
  1201. return True
  1202. except Exception as e:
  1203. logger.error(f"配置模板保存失败!\n{e}")
  1204. ui.notify(position="top", type="negative", message=f"配置模板保存失败!{e}")
  1205. return False
  1206. # 配置模板加载
  1207. def config_template_load(file_path: str):
  1208. try:
  1209. with open(file_path, 'r', encoding="utf-8") as config_file:
  1210. config_data = json.load(config_file)
  1211. # 将JSON数据保存到文件中
  1212. with open(config_path, "w", encoding="utf-8") as file:
  1213. json.dump(config_data, file, indent=2, ensure_ascii=False)
  1214. file.flush() # 刷新缓冲区,确保写入立即生效
  1215. logger.info("配置模板加载成功!重启后读取!想反悔就直接保存下当前配置,然后再重启!!!")
  1216. ui.notify(position="top", type="positive", message=f"配置模板加载成功!重启后读取!想反悔就直接保存下当前配置,然后再重启!!!")
  1217. return True
  1218. except Exception as e:
  1219. logger.error(f"配置模板读取失败!\n{e}")
  1220. ui.notify(position="top", type="negative", message=f"配置模板读取失败!{e}")
  1221. return False
  1222. """
  1223. 配置操作
  1224. """
  1225. # 配置检查
  1226. def check_config():
  1227. try:
  1228. # 通用配置 页面 配置正确性校验
  1229. if select_platform.value == 'bilibili2' and select_bilibili_login_type.value == 'cookie' and input_bilibili_cookie.value == '':
  1230. ui.notify(position="top", type="warning", message="请先前往 通用配置-哔哩哔哩,填写B站cookie")
  1231. return False
  1232. elif select_platform.value == 'bilibili2' and select_bilibili_login_type.value == 'open_live' and \
  1233. (input_bilibili_open_live_ACCESS_KEY_ID.value == '' or input_bilibili_open_live_ACCESS_KEY_SECRET.value == '' or \
  1234. input_bilibili_open_live_APP_ID.value == '' or input_bilibili_open_live_ROOM_OWNER_AUTH_CODE.value == ''):
  1235. ui.notify(position="top", type="warning", message="请先前往 通用配置-哔哩哔哩,填写开放平台配置")
  1236. return False
  1237. """
  1238. 针对配置情况进行提示
  1239. """
  1240. tip_config = f'平台:{platform_options[select_platform.value]} | ' +\
  1241. f'大语言模型:{chat_type_options[select_chat_type.value]} | ' +\
  1242. f'语音合成:{audio_synthesis_type_options[select_audio_synthesis_type.value]} | ' +\
  1243. f'虚拟身体:{visual_body_options[select_visual_body.value]}'
  1244. ui.notify(position="top", type="info", message=tip_config)
  1245. # 检测平台配置,进行提示
  1246. if select_platform.value == "dy":
  1247. ui.notify(position="top", type="warning", message="对接抖音平台时,请先开启抖音弹幕监听程序!直播间号不需要填写")
  1248. elif select_platform.value == "bilibili":
  1249. ui.notify(position="top", type="info", message="哔哩哔哩1 监听不是很稳定,推荐使用 哔哩哔哩2")
  1250. elif select_platform.value == "bilibili2":
  1251. if select_bilibili_login_type.value == "不登录":
  1252. ui.notify(position="top", type="warning", message="哔哩哔哩2 在不登录的情况下,无法获取用户完整的用户名")
  1253. if select_visual_body.value == "metahuman_stream":
  1254. ui.notify(position="top", type="warning", message="对接metahuman_stream时,语音合成由metahuman_stream托管,不受AI Vtuber控制,请自行参考官方文档对接TTS")
  1255. if not common.is_json_convertible(textarea_local_qa_text_json_file_content.value):
  1256. ui.notify(position="top", type="negative", message="本地问答json数据格式不正确,请检查JSON语法!")
  1257. return False
  1258. return True
  1259. except Exception as e:
  1260. ui.notify(position="top", type="negative", message=f"配置错误:{e}")
  1261. return False
  1262. """
  1263. .................................................................................................................................................................
  1264. .................................................................................................................................................................
  1265. .................................................................................................................................................................
  1266. .................................................................................................................................................................
  1267. .............................................................................................................:**.................................................
  1268. ........+++..........-++:....:++:...*##############:%%%%%%%%%#.....%%%%%%%%%%%%%%%%%%%%%%%.....%@#...........-@%..........+%%%%%%%%%%%%%+-----------:............
  1269. ........%@#..........=@@=....-@@=....::::%#:=@+::::.........%%.....%%.....%%.....%#.....%%......+@@*..#%%%%%%%@@%%%%%%%%....=@#.....%@-.*%@#######%@=............
  1270. ........%@#..........=@@=....-@@=........%*.-@+.............%%.....%@%%%%%@@%%%%%@@%%%%%@%........%%:........-@%............=@#.....%@-..#@-......#@-............
  1271. ........%@#..........=@@=....-@@=....%%%%@@%%@%%%%=.........%%.....::........#%=........::...................=@%:...........=@%#####%@-..=@=.....-%%.............
  1272. ........%@#..........=@@=....-@@=....%%..%*.-@+.=@=.........%%...%%%%%%%%%%%%@@%%%%%%%%%%%%*.:-----..#%%%%%%%%%%%%%%%%%@-...=@#-----%@-..:%#.....*@=.............
  1273. ........%@#..........=@@=....-@@=....%%.:%*.-@+.=@=.-%@@@@@@@%...............%@=.............+##%@%.....=%%+:..=@#....#%:...=@#.....%@-...#@-....%%..............
  1274. ........%@#..........=@@=....-@@=....%%.+@=.-@+.=@=.=@+.....##.......@%***************#@+.......=@%....-..:*%#.=@#....+*....=@#-----%@-...-%#...#@=..............
  1275. ........%@#..........=@@=....-@@=....%%+@#...*%%%@=.=@+..............@#===============*@+.......=@%...-#@%*:...+@*..........=@%*****%@-....*@=.=%*...............
  1276. ........#@%..........+@@:....-@@=....%%-*.......=@=.=@+..............@#-::::::::::::::+@+.......=@%......:**...*@+..........=@#.....%@-.....%@#%%................
  1277. ........*@@=........:%@#.....-@@=....%@%%%%%%%%%%@=.=@+......-*:.....@%%%%%%%%%%%%%%%%%@+.......=@%.:%@@@@@@@@@@@@@@@@@@%...=@#.....%@++*=...%@#.................
  1278. .........*@@%-.....*%@%......-@@=....%%.........=@=.-@+......+@=.....@*...............=@+.......=@%..:........%@*.........+#%@%%%@@@@@#+-..:%@%@%................
  1279. ..........:%%@@@@@@%%-.......-@@=....%%.........=@=.-@+......%@-.....@%%%%%%%%%%%%%%%%%@+.......=@%#@%:....:#@%*%@%*......+*=:......%@-...#@%..:%@*..............
  1280. .....................................%@@@@@@@@@@@@=.:%@#+==+%@%.....:@#...............=@*.......#@@#-..:+%@@%-....=#@@#-............%@--%@%-.....+%@%=...........
  1281. .....................................%%.........=%=...=*****+:..-***************************-...-+...#@%#+:..........-#%:...........%@=%#:.........+#............
  1282. .................................................................................................................................................................
  1283. .................................................................................................................................................................
  1284. .................................................................................................................................................................
  1285. .................................................................................................................................................................
  1286. """
  1287. # 读取webui配置到dict变量
  1288. def webui_config_to_dict(config_data):
  1289. """读取webui配置到dict变量
  1290. Args:
  1291. config_data (dict): 从本地配置文件读取的dict数据
  1292. """
  1293. def common_textarea_handle(content):
  1294. """通用的textEdit 多行文本内容处理
  1295. Args:
  1296. content (str): 原始多行文本内容
  1297. Returns:
  1298. _type_: 处理好的多行文本内容
  1299. """
  1300. ret = [token.strip() for token in content.split("\n") if token.strip()]
  1301. return ret
  1302. # 类型处理函数
  1303. def handle_int(value):
  1304. if value.value == '' or value.value is None:
  1305. return 0
  1306. return int(value.value)
  1307. def handle_float(value):
  1308. if value.value == '' or value.value is None:
  1309. return 0
  1310. return round(float(value.value), 2)
  1311. def handle_string(value):
  1312. return str(value.value)
  1313. def handle_bool(value):
  1314. return bool(value.value)
  1315. def handle_textarea(value):
  1316. return common_textarea_handle(value.value)
  1317. # 处理器映射
  1318. type_handlers = {
  1319. 'int': handle_int,
  1320. 'float': handle_float,
  1321. 'str': handle_string,
  1322. 'bool': handle_bool,
  1323. 'textarea': handle_textarea,
  1324. }
  1325. def update_nested_dict(target, keys, value):
  1326. """递归更新嵌套字典"""
  1327. if len(keys) == 1:
  1328. target[keys[0]] = value
  1329. return
  1330. if keys[0] not in target:
  1331. target[keys[0]] = {}
  1332. update_nested_dict(target[keys[0]], keys[1:], value)
  1333. def process_config_mapping(config_data, mapping, show_card_check=None):
  1334. """处理配置映射,支持不同层级的嵌套"""
  1335. def recurse_mapping(current_mapping, current_path=[]):
  1336. for key, value in current_mapping.items():
  1337. new_path = current_path + [key]
  1338. if isinstance(value, dict):
  1339. recurse_mapping(value, new_path)
  1340. else:
  1341. component, type_name = value
  1342. handler = type_handlers[type_name]
  1343. processed_value = handler(component)
  1344. update_nested_dict(config_data, new_path, processed_value)
  1345. recurse_mapping(mapping)
  1346. return config_data
  1347. def update_config(config_mapping, config, config_data, type="common_config"):
  1348. # 处理常规配置
  1349. for section, section_mapping in config_mapping.items():
  1350. if type is not None:
  1351. if config.get("webui", "show_card", type, section):
  1352. if section not in config_data:
  1353. config_data[section] = {}
  1354. process_config_mapping(config_data[section], section_mapping)
  1355. else:
  1356. if section not in config_data:
  1357. config_data[section] = {}
  1358. process_config_mapping(config_data[section], section_mapping)
  1359. return config_data
  1360. try:
  1361. """
  1362. 通用配置
  1363. """
  1364. if True:
  1365. config_data["platform"] = select_platform.value
  1366. config_data["room_display_id"] = input_room_display_id.value
  1367. config_data["chat_type"] = select_chat_type.value
  1368. config_data["visual_body"] = select_visual_body.value
  1369. config_data["need_lang"] = select_need_lang.value
  1370. config_data["before_prompt"] = input_before_prompt.value
  1371. config_data["after_prompt"] = input_after_prompt.value
  1372. config_data["audio_synthesis_type"] = select_audio_synthesis_type.value
  1373. config_mapping = {
  1374. "comment_template": {
  1375. "enable": (switch_comment_template_enable, 'bool'),
  1376. "copywriting": (input_comment_template_copywriting, 'str'),
  1377. },
  1378. "reply_template": {
  1379. "enable": (switch_reply_template_enable, 'bool'),
  1380. "username_max_len": (input_reply_template_username_max_len, 'int'),
  1381. "copywriting": (textarea_reply_template_copywriting, 'textarea'),
  1382. },
  1383. "bilibili": {
  1384. "login_type": (select_bilibili_login_type, 'str'),
  1385. "cookie": (input_bilibili_cookie, 'str'),
  1386. "ac_time_value": (input_bilibili_ac_time_value, 'str'),
  1387. "username": (input_bilibili_username, 'str'),
  1388. "password": (input_bilibili_password, 'str'),
  1389. "open_live": {
  1390. "ACCESS_KEY_ID": (input_bilibili_open_live_ACCESS_KEY_ID, 'str'),
  1391. "ACCESS_KEY_SECRET": (input_bilibili_open_live_ACCESS_KEY_SECRET, 'str'),
  1392. "APP_ID": (input_bilibili_open_live_APP_ID, 'int'),
  1393. "ROOM_OWNER_AUTH_CODE": (input_bilibili_open_live_ROOM_OWNER_AUTH_CODE, 'str'),
  1394. },
  1395. },
  1396. "twitch": {
  1397. "token": (input_twitch_token, 'str'),
  1398. "user": (input_twitch_user, 'str'),
  1399. "proxy_server": (input_twitch_proxy_server, 'str'),
  1400. "proxy_port": (input_twitch_proxy_port, 'str'),
  1401. },
  1402. }
  1403. config_data = update_config(config_mapping, config, config_data, None)
  1404. # 音频播放
  1405. if config.get("webui", "show_card", "common_config", "play_audio"):
  1406. # audio_player
  1407. config_data["audio_player"]["api_ip_port"] = input_audio_player_api_ip_port.value
  1408. # 日志
  1409. if config.get("webui", "show_card", "common_config", "log"):
  1410. config_data["comment_log_type"] = select_comment_log_type.value
  1411. config_data["captions"]["enable"] = switch_captions_enable.value
  1412. config_data["captions"]["file_path"] = input_captions_file_path.value
  1413. config_data["captions"]["raw_file_path"] = input_captions_raw_file_path.value
  1414. # 配置映射
  1415. config_mapping = {
  1416. "play_audio": {
  1417. "enable": (switch_play_audio_enable, 'bool'),
  1418. "text_split_enable": (switch_play_audio_text_split_enable, 'bool'),
  1419. "info_to_callback": (switch_play_audio_info_to_callback, 'bool'),
  1420. "interval_num_min": (input_play_audio_interval_num_min, 'int'),
  1421. "interval_num_max": (input_play_audio_interval_num_max, 'int'),
  1422. "normal_interval_min": (input_play_audio_normal_interval_min, 'float'),
  1423. "normal_interval_max": (input_play_audio_normal_interval_max, 'float'),
  1424. "out_path": (input_play_audio_out_path,'str'),
  1425. "player": (select_play_audio_player,'str'),
  1426. },
  1427. "read_comment": {
  1428. "enable": (switch_read_comment_enable, 'bool'),
  1429. "read_username_enable": (switch_read_comment_read_username_enable, 'bool'),
  1430. "username_max_len": (input_read_comment_username_max_len, 'int'),
  1431. "voice_change": (switch_read_comment_voice_change, 'bool'),
  1432. "read_username_copywriting": (textarea_read_comment_read_username_copywriting, 'textarea'),
  1433. "periodic_trigger": {
  1434. "enable": (switch_read_comment_periodic_trigger_enable, 'bool'),
  1435. "periodic_time_min": (input_read_comment_periodic_trigger_periodic_time_min, 'int'),
  1436. "periodic_time_max": (input_read_comment_periodic_trigger_periodic_time_max, 'int'),
  1437. "trigger_num_min": (input_read_comment_periodic_trigger_trigger_num_min, 'int'),
  1438. "trigger_num_max": (input_read_comment_periodic_trigger_trigger_num_max, 'int'),
  1439. },
  1440. },
  1441. "local_qa": {
  1442. "periodic_trigger": {
  1443. "enable": (switch_local_qa_periodic_trigger_enable, 'bool'),
  1444. "periodic_time_min": (input_local_qa_periodic_trigger_periodic_time_min, 'int'),
  1445. "periodic_time_max": (input_local_qa_periodic_trigger_periodic_time_max, 'int'),
  1446. "trigger_num_min": (input_local_qa_periodic_trigger_trigger_num_min, 'int'),
  1447. "trigger_num_max": (input_local_qa_periodic_trigger_trigger_num_max, 'int'),
  1448. },
  1449. "text": {
  1450. "enable": (switch_local_qa_text_enable, 'bool'),
  1451. "type": (select_local_qa_text_type, 'str'),
  1452. "file_path": (input_local_qa_text_file_path, 'str'),
  1453. "similarity": (input_local_qa_text_similarity, 'float'),
  1454. "username_max_len": (input_local_qa_text_username_max_len, 'int'),
  1455. },
  1456. "audio": {
  1457. "enable": (switch_local_qa_audio_enable, 'bool'),
  1458. "file_path": (input_local_qa_audio_file_path, 'str'),
  1459. "similarity": (input_local_qa_audio_similarity, 'float'),
  1460. },
  1461. },
  1462. "filter": {
  1463. "before_must_str": (textarea_filter_before_must_str, 'textarea'),
  1464. "after_must_str": (textarea_filter_after_must_str, 'textarea'),
  1465. "before_filter_str": (textarea_filter_before_filter_str, 'textarea'),
  1466. "after_filter_str": (textarea_filter_after_filter_str, 'textarea'),
  1467. "before_must_str_for_llm": (textarea_filter_before_must_str_for_llm, 'textarea'),
  1468. "after_must_str_for_llm": (textarea_filter_after_must_str_for_llm, 'textarea'),
  1469. "badwords": {
  1470. "enable": (switch_filter_badwords_enable, 'bool'),
  1471. "discard": (switch_filter_badwords_discard, 'bool'),
  1472. "path": (input_filter_badwords_path,'str'),
  1473. "bad_pinyin_path": (input_filter_badwords_bad_pinyin_path,'str'),
  1474. "replace": (input_filter_badwords_replace,'str'),
  1475. },
  1476. "username_convert_digits_to_chinese": (switch_filter_username_convert_digits_to_chinese, 'bool'),
  1477. "emoji": (switch_filter_emoji, 'bool'),
  1478. "max_len": (input_filter_max_len, 'int'),
  1479. "max_char_len": (input_filter_max_char_len, 'int'),
  1480. "comment_forget_duration": (input_filter_comment_forget_duration, 'float'),
  1481. "comment_forget_reserve_num": (input_filter_comment_forget_reserve_num, 'int'),
  1482. "gift_forget_duration": (input_filter_gift_forget_duration, 'float'),
  1483. "gift_forget_reserve_num": (input_filter_gift_forget_reserve_num, 'int'),
  1484. "entrance_forget_duration": (input_filter_entrance_forget_duration, 'float'),
  1485. "entrance_forget_reserve_num": (input_filter_entrance_forget_reserve_num, 'int'),
  1486. "follow_forget_duration": (input_filter_follow_forget_duration, 'float'),
  1487. "follow_forget_reserve_num": (input_filter_follow_forget_reserve_num, 'int'),
  1488. "talk_forget_duration": (input_filter_talk_forget_duration, 'float'),
  1489. "talk_forget_reserve_num": (input_filter_talk_forget_reserve_num, 'int'),
  1490. "schedule_forget_duration": (input_filter_schedule_forget_duration, 'float'),
  1491. "schedule_forget_reserve_num": (input_filter_schedule_forget_reserve_num, 'int'),
  1492. "idle_time_task_forget_duration": (input_filter_idle_time_task_forget_duration, 'float'),
  1493. "idle_time_task_forget_reserve_num": (input_filter_idle_time_task_forget_reserve_num, 'int'),
  1494. "image_recognition_schedule_forget_duration": (input_filter_image_recognition_schedule_forget_duration, 'float'),
  1495. "image_recognition_schedule_forget_reserve_num": (input_filter_image_recognition_schedule_forget_reserve_num, 'int'),
  1496. "limited_time_deduplication": {
  1497. "enable": (switch_filter_limited_time_deduplication_enable, 'bool'),
  1498. "comment": (input_filter_limited_time_deduplication_comment, 'int'),
  1499. "gift": (input_filter_limited_time_deduplication_gift, 'int'),
  1500. "entrance": (input_filter_limited_time_deduplication_entrance, 'int'),
  1501. },
  1502. "message_queue_max_len": (input_filter_message_queue_max_len, 'int'),
  1503. "voice_tmp_path_queue_max_len": (input_filter_voice_tmp_path_queue_max_len, 'int'),
  1504. "voice_tmp_path_queue_min_start_play": (input_filter_voice_tmp_path_queue_min_start_play, 'int'),
  1505. "priority_mapping": {
  1506. "idle_time_task": (input_filter_priority_mapping_idle_time_task, 'int'),
  1507. "image_recognition_schedule": (input_filter_priority_mapping_image_recognition_schedule, 'int'),
  1508. "local_qa_audio": (input_filter_priority_mapping_local_qa_audio, 'int'),
  1509. "comment": (input_filter_priority_mapping_comment, 'int'),
  1510. "song": (input_filter_priority_mapping_song, 'int'),
  1511. "read_comment": (input_filter_priority_mapping_read_comment, 'int'),
  1512. "entrance": (input_filter_priority_mapping_entrance, 'int'),
  1513. "gift": (input_filter_priority_mapping_gift, 'int'),
  1514. "follow": (input_filter_priority_mapping_follow, 'int'),
  1515. "talk": (input_filter_priority_mapping_talk, 'int'),
  1516. "reread": (input_filter_priority_mapping_reread, 'int'),
  1517. "key_mapping": (input_filter_priority_mapping_key_mapping, 'int'),
  1518. "integral": (input_filter_priority_mapping_integral, 'int'),
  1519. "reread_top_priority": (input_filter_priority_mapping_reread_top_priority, 'int'),
  1520. "copywriting": (input_filter_priority_mapping_copywriting, 'int'),
  1521. "abnormal_alarm": (input_filter_priority_mapping_abnormal_alarm, 'int'),
  1522. "trends_copywriting": (input_filter_priority_mapping_trends_copywriting, 'int'),
  1523. "schedule": (input_filter_priority_mapping_schedule, 'int'),
  1524. },
  1525. "blacklist": {
  1526. "enable": (switch_filter_blacklist_enable, 'bool'),
  1527. "username": (textarea_filter_blacklist_username, 'textarea'),
  1528. }
  1529. },
  1530. "thanks": {
  1531. "username_max_len": (input_thanks_username_max_len, 'int'),
  1532. "entrance_enable": (switch_thanks_entrance_enable, 'bool'),
  1533. "entrance_random": (switch_thanks_entrance_random, 'bool'),
  1534. "entrance_copy": (textarea_thanks_entrance_copy, 'textarea'),
  1535. "entrance": {
  1536. "periodic_trigger": {
  1537. "enable": (switch_thanks_entrance_periodic_trigger_enable, 'bool'),
  1538. "periodic_time_min": (input_thanks_entrance_periodic_trigger_periodic_time_min, 'int'),
  1539. "periodic_time_max": (input_thanks_entrance_periodic_trigger_periodic_time_max, 'int'),
  1540. "trigger_num_min": (input_thanks_entrance_periodic_trigger_trigger_num_min, 'int'),
  1541. "trigger_num_max": (input_thanks_entrance_periodic_trigger_trigger_num_max, 'int'),
  1542. }
  1543. },
  1544. "gift_enable": (switch_thanks_gift_enable, 'bool'),
  1545. "gift_random": (switch_thanks_gift_random, 'bool'),
  1546. "gift_copy": (textarea_thanks_gift_copy, 'textarea'),
  1547. "gift": {
  1548. "periodic_trigger": {
  1549. "enable": (switch_thanks_gift_periodic_trigger_enable, 'bool'),
  1550. "periodic_time_min": (input_thanks_gift_periodic_trigger_periodic_time_min, 'int'),
  1551. "periodic_time_max": (input_thanks_gift_periodic_trigger_periodic_time_max, 'int'),
  1552. "trigger_num_min": (input_thanks_gift_periodic_trigger_trigger_num_min, 'int'),
  1553. "trigger_num_max": (input_thanks_gift_periodic_trigger_trigger_num_max, 'int'),
  1554. }
  1555. },
  1556. "follow_enable": (switch_thanks_follow_enable, 'bool'),
  1557. "follow_random": (switch_thanks_follow_random, 'bool'),
  1558. "follow_copy": (textarea_thanks_follow_copy, 'textarea'),
  1559. "follow": {
  1560. "periodic_trigger": {
  1561. "enable": (switch_thanks_follow_periodic_trigger_enable, 'bool'),
  1562. "periodic_time_min": (input_thanks_follow_periodic_trigger_periodic_time_min, 'int'),
  1563. "periodic_time_max": (input_thanks_follow_periodic_trigger_periodic_time_max, 'int'),
  1564. "trigger_num_min": (input_thanks_follow_periodic_trigger_trigger_num_min, 'int'),
  1565. "trigger_num_max": (input_thanks_follow_periodic_trigger_trigger_num_max, 'int'),
  1566. }
  1567. },
  1568. "lowest_price": (input_thanks_lowest_price, 'float')
  1569. },
  1570. "audio_random_speed": {
  1571. "normal": {
  1572. "enable": (switch_audio_random_speed_normal_enable, 'bool'),
  1573. "speed_min": (input_audio_random_speed_normal_speed_min, 'float'),
  1574. "speed_max": (input_audio_random_speed_normal_speed_max, 'float'),
  1575. },
  1576. "copywriting": {
  1577. "enable": (switch_audio_random_speed_copywriting_enable, 'bool'),
  1578. "speed_min": (input_audio_random_speed_copywriting_speed_min, 'float'),
  1579. "speed_max": (input_audio_random_speed_copywriting_speed_max, 'float'),
  1580. },
  1581. },
  1582. "choose_song": {
  1583. "enable": (switch_choose_song_enable, 'bool'),
  1584. "start_cmd": (textarea_choose_song_start_cmd,'textarea'),
  1585. "stop_cmd": (textarea_choose_song_stop_cmd,'textarea'),
  1586. "random_cmd": (textarea_choose_song_random_cmd,'textarea'),
  1587. "song_path": (input_choose_song_song_path,'str'),
  1588. "match_fail_copy": (input_choose_song_match_fail_copy,'str'),
  1589. "similarity": (input_choose_song_similarity,'float'),
  1590. },
  1591. "sd": {
  1592. "enable": (switch_sd_enable, 'bool'),
  1593. "translate_type": (select_sd_translate_type, 'str'),
  1594. "prompt_llm": {
  1595. "type": (select_sd_prompt_llm_type, 'str'),
  1596. "before_prompt": (input_sd_prompt_llm_before_prompt, 'str'),
  1597. "after_prompt": (input_sd_prompt_llm_after_prompt, 'str'),
  1598. },
  1599. "trigger": (input_sd_trigger, 'str'),
  1600. "ip": (input_sd_ip, 'str'),
  1601. "port": (input_sd_port, 'int'),
  1602. "negative_prompt": (input_sd_negative_prompt, 'str'),
  1603. "seed": (input_sd_seed, 'float'),
  1604. "styles": (textarea_sd_styles, 'textarea'),
  1605. "cfg_scale": (input_sd_cfg_scale, 'int'),
  1606. "steps": (input_sd_steps, 'int'),
  1607. "hr_resize_x": (input_sd_hr_resize_x, 'int'),
  1608. "hr_resize_y": (input_sd_hr_resize_y, 'int'),
  1609. "enable_hr": (switch_sd_enable_hr, 'bool'),
  1610. "hr_scale": (input_sd_hr_scale, 'int'),
  1611. "hr_second_pass_steps": (input_sd_hr_second_pass_steps, 'int'),
  1612. "denoising_strength": (input_sd_denoising_strength, 'float'),
  1613. "save_enable": (switch_sd_save_enable, 'bool'),
  1614. "loop_cover": (switch_sd_loop_cover, 'bool'),
  1615. "save_path": (input_sd_save_path, 'str'),
  1616. },
  1617. "search_online": {
  1618. "enable": (switch_search_online_enable, 'bool'),
  1619. "keyword_enable": (switch_search_online_keyword_enable, 'bool'),
  1620. "before_keyword": (textarea_search_online_before_keyword, 'textarea'),
  1621. "engine": (select_search_online_engine, 'str'),
  1622. "engine_id": (input_search_online_engine_id, 'int'),
  1623. "count": (input_search_online_count, 'int'),
  1624. "resp_template": (input_search_online_resp_template, 'str'),
  1625. "http_proxy": (input_search_online_http_proxy, 'str'),
  1626. "https_proxy": (input_search_online_https_proxy, 'str'),
  1627. },
  1628. "web_captions_printer": {
  1629. "enable": (switch_web_captions_printer_enable, 'bool'),
  1630. "api_ip_port": (input_web_captions_printer_api_ip_port, 'str'),
  1631. },
  1632. "database": {
  1633. "path": (input_database_path, 'str'),
  1634. "comment_enable": (switch_database_comment_enable, 'bool'),
  1635. "entrance_enable": (switch_database_entrance_enable, 'bool'),
  1636. "gift_enable": (switch_database_gift_enable, 'bool'),
  1637. },
  1638. "abnormal_alarm": {
  1639. "platform": {
  1640. "enable": (switch_abnormal_alarm_platform_enable, 'bool'),
  1641. "type": (select_abnormal_alarm_platform_type, 'str'),
  1642. "start_alarm_error_num": (input_abnormal_alarm_platform_start_alarm_error_num, 'int'),
  1643. "auto_restart_error_num": (input_abnormal_alarm_platform_auto_restart_error_num, 'int'),
  1644. "local_audio_path": (input_abnormal_alarm_platform_local_audio_path, 'str'),
  1645. },
  1646. "llm": {
  1647. "enable": (switch_abnormal_alarm_llm_enable, 'bool'),
  1648. "type": (select_abnormal_alarm_llm_type, 'str'),
  1649. "start_alarm_error_num": (input_abnormal_alarm_llm_start_alarm_error_num, 'int'),
  1650. "auto_restart_error_num": (input_abnormal_alarm_llm_auto_restart_error_num, 'int'),
  1651. "local_audio_path": (input_abnormal_alarm_llm_local_audio_path, 'str'),
  1652. },
  1653. "tts": {
  1654. "enable": (switch_abnormal_alarm_tts_enable, 'bool'),
  1655. "type": (select_abnormal_alarm_tts_type, 'str'),
  1656. "start_alarm_error_num": (input_abnormal_alarm_tts_start_alarm_error_num, 'int'),
  1657. "auto_restart_error_num": (input_abnormal_alarm_tts_auto_restart_error_num, 'int'),
  1658. "local_audio_path": (input_abnormal_alarm_tts_local_audio_path, 'str'),
  1659. },
  1660. "svc": {
  1661. "enable": (switch_abnormal_alarm_svc_enable, 'bool'),
  1662. "type": (select_abnormal_alarm_svc_type, 'str'),
  1663. "start_alarm_error_num": (input_abnormal_alarm_svc_start_alarm_error_num, 'int'),
  1664. "auto_restart_error_num": (input_abnormal_alarm_svc_auto_restart_error_num, 'int'),
  1665. "local_audio_path": (input_abnormal_alarm_svc_local_audio_path, 'str'),
  1666. },
  1667. "visual_body": {
  1668. "enable": (switch_abnormal_alarm_visual_body_enable, 'bool'),
  1669. "type": (select_abnormal_alarm_visual_body_type, 'str'),
  1670. "start_alarm_error_num": (input_abnormal_alarm_visual_body_start_alarm_error_num, 'int'),
  1671. "auto_restart_error_num": (input_abnormal_alarm_visual_body_auto_restart_error_num, 'int'),
  1672. "local_audio_path": (input_abnormal_alarm_visual_body_local_audio_path, 'str'),
  1673. },
  1674. "other": {
  1675. "enable": (switch_abnormal_alarm_other_enable, 'bool'),
  1676. "type": (select_abnormal_alarm_other_type, 'str'),
  1677. "start_alarm_error_num": (input_abnormal_alarm_other_start_alarm_error_num, 'int'),
  1678. "auto_restart_error_num": (input_abnormal_alarm_other_auto_restart_error_num, 'int'),
  1679. "local_audio_path": (input_abnormal_alarm_other_local_audio_path, 'str'),
  1680. },
  1681. },
  1682. }
  1683. config_data = update_config(config_mapping, config, config_data, "common_config")
  1684. # 定时任务
  1685. if config.get("webui", "show_card", "common_config", "schedule"):
  1686. tmp_arr = []
  1687. # logger.info(schedule_var)
  1688. for index in range(len(schedule_var) // 4):
  1689. tmp_json = {
  1690. "enable": False,
  1691. "time_min": 60,
  1692. "time_max": 120,
  1693. "copy": []
  1694. }
  1695. tmp_json["enable"] = schedule_var[str(4 * index)].value
  1696. tmp_json["time_min"] = round(float(schedule_var[str(4 * index + 1)].value), 1)
  1697. tmp_json["time_max"] = round(float(schedule_var[str(4 * index + 2)].value), 1)
  1698. tmp_json["copy"] = common_textarea_handle(schedule_var[str(4 * index + 3)].value)
  1699. tmp_arr.append(tmp_json)
  1700. # logger.info(tmp_arr)
  1701. config_data["schedule"] = tmp_arr
  1702. # 闲时任务
  1703. if config.get("webui", "show_card", "common_config", "idle_time_task"):
  1704. config_data["idle_time_task"]["enable"] = switch_idle_time_task_enable.value
  1705. config_data["idle_time_task"]["type"] = select_idle_time_task_type.value
  1706. config_data["idle_time_task"]["min_msg_queue_len_to_trigger"] = int(input_idle_time_task_idle_min_msg_queue_len_to_trigger.value)
  1707. config_data["idle_time_task"]["min_audio_queue_len_to_trigger"] = int(input_idle_time_task_idle_min_audio_queue_len_to_trigger.value)
  1708. config_data["idle_time_task"]["idle_time_min"] = int(input_idle_time_task_idle_time_min.value)
  1709. config_data["idle_time_task"]["idle_time_max"] = int(input_idle_time_task_idle_time_max.value)
  1710. config_data["idle_time_task"]["wait_play_audio_num_threshold"] = int(input_idle_time_task_wait_play_audio_num_threshold.value)
  1711. config_data["idle_time_task"]["idle_time_reduce_to"] = int(input_idle_time_task_idle_time_reduce_to.value)
  1712. tmp_arr = []
  1713. for index in range(len(idle_time_task_trigger_type_var)):
  1714. if idle_time_task_trigger_type_var[str(index)].value:
  1715. tmp_arr.append(common.find_keys_by_value(idle_time_task_trigger_type_mapping, idle_time_task_trigger_type_var[str(index)].text)[0])
  1716. # logger.info(tmp_arr)
  1717. config_data["idle_time_task"]["trigger_type"] = tmp_arr
  1718. config_data["idle_time_task"]["comment"]["enable"] = switch_idle_time_task_comment_enable.value
  1719. config_data["idle_time_task"]["comment"]["random"] = switch_idle_time_task_comment_random.value
  1720. config_data["idle_time_task"]["copywriting"]["copy"] = common_textarea_handle(textarea_idle_time_task_copywriting_copy.value)
  1721. config_data["idle_time_task"]["copywriting"]["enable"] = switch_idle_time_task_copywriting_enable.value
  1722. config_data["idle_time_task"]["copywriting"]["random"] = switch_idle_time_task_copywriting_random.value
  1723. config_data["idle_time_task"]["comment"]["copy"] = common_textarea_handle(textarea_idle_time_task_comment_copy.value)
  1724. config_data["idle_time_task"]["local_audio"]["enable"] = switch_idle_time_task_local_audio_enable.value
  1725. config_data["idle_time_task"]["local_audio"]["random"] = switch_idle_time_task_local_audio_random.value
  1726. config_data["idle_time_task"]["local_audio"]["path"] = common_textarea_handle(textarea_idle_time_task_local_audio_path.value)
  1727. # 动态文案
  1728. if config.get("webui", "show_card", "common_config", "trends_copywriting"):
  1729. config_data["trends_copywriting"]["enable"] = switch_trends_copywriting_enable.value
  1730. config_data["trends_copywriting"]["llm_type"] = select_trends_copywriting_llm_type.value
  1731. config_data["trends_copywriting"]["random_play"] = switch_trends_copywriting_random_play.value
  1732. config_data["trends_copywriting"]["play_interval"] = int(input_trends_copywriting_play_interval.value)
  1733. tmp_arr = []
  1734. for index in range(len(trends_copywriting_copywriting_var) // 3):
  1735. tmp_json = {
  1736. "folder_path": "",
  1737. "prompt_change_enable": False,
  1738. "prompt_change_content": ""
  1739. }
  1740. tmp_json["folder_path"] = trends_copywriting_copywriting_var[str(3 * index)].value
  1741. tmp_json["prompt_change_enable"] = trends_copywriting_copywriting_var[str(3 * index + 1)].value
  1742. tmp_json["prompt_change_content"] = trends_copywriting_copywriting_var[str(3 * index + 2)].value
  1743. tmp_arr.append(tmp_json)
  1744. # logger.info(tmp_arr)
  1745. config_data["trends_copywriting"]["copywriting"] = tmp_arr
  1746. # 按键映射
  1747. if config.get("webui", "show_card", "common_config", "key_mapping"):
  1748. config_data["key_mapping"]["enable"] = switch_key_mapping_enable.value
  1749. config_data["key_mapping"]["type"] = select_key_mapping_type.value
  1750. config_data["key_mapping"]["key_trigger_type"] = select_key_mapping_key_trigger_type.value
  1751. config_data["key_mapping"]["key_single_sentence_trigger_once"] = switch_key_mapping_key_single_sentence_trigger_once_enable.value
  1752. config_data["key_mapping"]["copywriting_trigger_type"] = select_key_mapping_copywriting_trigger_type.value
  1753. config_data["key_mapping"]["copywriting_single_sentence_trigger_once"] = switch_key_mapping_copywriting_single_sentence_trigger_once_enable.value
  1754. config_data["key_mapping"]["local_audio_trigger_type"] = select_key_mapping_local_audio_trigger_type.value
  1755. config_data["key_mapping"]["local_audio_single_sentence_trigger_once"] = switch_key_mapping_local_audio_single_sentence_trigger_once_enable.value
  1756. config_data["key_mapping"]["serial_trigger_type"] = select_key_mapping_serial_trigger_type.value
  1757. config_data["key_mapping"]["serial_single_sentence_trigger_once"] = switch_key_mapping_serial_single_sentence_trigger_once_enable.value
  1758. config_data["key_mapping"]["img_path_trigger_type"] = select_key_mapping_img_path_trigger_type.value
  1759. config_data["key_mapping"]["img_path_single_sentence_trigger_once"] = switch_key_mapping_img_path_single_sentence_trigger_once_enable.value
  1760. config_data["key_mapping"]["start_cmd"] = input_key_mapping_start_cmd.value
  1761. tmp_arr = []
  1762. # logger.info(key_mapping_config_var)
  1763. num = 9
  1764. for index in range(len(key_mapping_config_var) // num):
  1765. tmp_json = {
  1766. "keywords": [],
  1767. "gift": [],
  1768. "keys": [],
  1769. "similarity": 0.8,
  1770. "copywriting": [],
  1771. "serial_name": "",
  1772. "serial_send_data": [],
  1773. "img_path": []
  1774. }
  1775. tmp_json["keywords"] = common_textarea_handle(key_mapping_config_var[str(num * index)].value)
  1776. tmp_json["gift"] = common_textarea_handle(key_mapping_config_var[str(num * index + 1)].value)
  1777. tmp_json["keys"] = common_textarea_handle(key_mapping_config_var[str(num * index + 2)].value)
  1778. tmp_json["similarity"] = key_mapping_config_var[str(num * index + 3)].value
  1779. tmp_json["copywriting"] = common_textarea_handle(key_mapping_config_var[str(num * index + 4)].value)
  1780. tmp_json["local_audio"] = common_textarea_handle(key_mapping_config_var[str(num * index + 5)].value)
  1781. tmp_json["serial_name"] = key_mapping_config_var[str(num * index + 6)].value
  1782. tmp_json["serial_send_data"] = common_textarea_handle(key_mapping_config_var[str(num * index + 7)].value)
  1783. tmp_json["img_path"] = common_textarea_handle(key_mapping_config_var[str(num * index + 8)].value)
  1784. tmp_arr.append(tmp_json)
  1785. # logger.info(tmp_arr)
  1786. config_data["key_mapping"]["config"] = tmp_arr
  1787. # 自定义命令
  1788. if config.get("webui", "show_card", "common_config", "custom_cmd"):
  1789. config_data["custom_cmd"]["enable"] = switch_custom_cmd_enable.value
  1790. config_data["custom_cmd"]["type"] = select_custom_cmd_type.value
  1791. tmp_arr = []
  1792. # logger.info(custom_cmd_config_var)
  1793. for index in range(len(custom_cmd_config_var) // 7):
  1794. tmp_json = {
  1795. "keywords": [],
  1796. "similarity": 1,
  1797. "api_url": "",
  1798. "api_type": "",
  1799. "resp_data_type": "",
  1800. "data_analysis": "",
  1801. "resp_template": ""
  1802. }
  1803. tmp_json["keywords"] = common_textarea_handle(custom_cmd_config_var[str(7 * index)].value)
  1804. tmp_json["similarity"] = float(custom_cmd_config_var[str(7 * index + 1)].value)
  1805. tmp_json["api_url"] = custom_cmd_config_var[str(7 * index + 2)].value
  1806. tmp_json["api_type"] = custom_cmd_config_var[str(7 * index + 3)].value
  1807. tmp_json["resp_data_type"] = custom_cmd_config_var[str(7 * index + 4)].value
  1808. tmp_json["data_analysis"] = custom_cmd_config_var[str(7 * index + 5)].value
  1809. tmp_json["resp_template"] = custom_cmd_config_var[str(7 * index + 6)].value
  1810. tmp_arr.append(tmp_json)
  1811. # logger.info(tmp_arr)
  1812. config_data["custom_cmd"]["config"] = tmp_arr
  1813. # 动态配置
  1814. if config.get("webui", "show_card", "common_config", "trends_config"):
  1815. config_data["trends_config"]["enable"] = switch_trends_config_enable.value
  1816. tmp_arr = []
  1817. # logger.info(trends_config_path_var)
  1818. for index in range(len(trends_config_path_var) // 2):
  1819. tmp_json = {
  1820. "online_num": "0-999999999",
  1821. "path": "config.json"
  1822. }
  1823. tmp_json["online_num"] = trends_config_path_var[str(2 * index)].value
  1824. tmp_json["path"] = trends_config_path_var[str(2 * index + 1)].value
  1825. tmp_arr.append(tmp_json)
  1826. # logger.info(tmp_arr)
  1827. config_data["trends_config"]["path"] = tmp_arr
  1828. # 联动程序
  1829. if config.get("webui", "show_card", "common_config", "coordination_program"):
  1830. tmp_arr = []
  1831. for index in range(len(coordination_program_var) // 4):
  1832. tmp_json = {
  1833. "enable": True,
  1834. "name": "",
  1835. "executable": "",
  1836. "parameters": []
  1837. }
  1838. tmp_json["enable"] = coordination_program_var[str(4 * index)].value
  1839. tmp_json["name"] = coordination_program_var[str(4 * index + 1)].value
  1840. tmp_json["executable"] = coordination_program_var[str(4 * index + 2)].value
  1841. tmp_json["parameters"] = common_textarea_handle(coordination_program_var[str(4 * index + 3)].value)
  1842. tmp_arr.append(tmp_json)
  1843. # logger.info(tmp_arr)
  1844. config_data["coordination_program"] = tmp_arr
  1845. """
  1846. LLM
  1847. """
  1848. if True:
  1849. if config.get("webui", "show_card", "llm", "chatgpt"):
  1850. config_data["openai"]["api"] = input_openai_api.value
  1851. config_data["openai"]["api_key"] = common_textarea_handle(textarea_openai_api_key.value)
  1852. # logger.info(select_chatgpt_model.value)
  1853. config_mapping = {
  1854. "chatgpt": {
  1855. "model": (select_chatgpt_model, 'str'),
  1856. "temperature": (input_chatgpt_temperature, 'float'),
  1857. "max_tokens": (input_chatgpt_max_tokens, 'int'),
  1858. "top_p": (input_chatgpt_top_p, 'float'),
  1859. "presence_penalty": (input_chatgpt_presence_penalty, 'float'),
  1860. "frequency_penalty": (input_chatgpt_frequency_penalty, 'float'),
  1861. "preset": (input_chatgpt_preset, 'str'),
  1862. "stream": (switch_chatgpt_stream, 'bool'),
  1863. },
  1864. "claude": {
  1865. "slack_user_token": (input_claude_slack_user_token, 'str'),
  1866. "bot_user_id": (input_claude_bot_user_id, 'str'),
  1867. },
  1868. "chatglm": {
  1869. "api_ip_port": (input_chatglm_api_ip_port, 'str'),
  1870. "max_length": (input_chatglm_max_length, 'int'),
  1871. "top_p": (input_chatglm_top_p, 'float'),
  1872. "temperature": (input_chatglm_temperature, 'float'),
  1873. "history_enable": (switch_chatglm_history_enable, 'bool'),
  1874. "history_max_len": (input_chatglm_history_max_len, 'int'),
  1875. },
  1876. "qwen": {
  1877. "api_ip_port": (input_qwen_api_ip_port, 'str'),
  1878. "max_length": (input_qwen_max_length, 'int'),
  1879. "top_p": (input_qwen_top_p, 'float'),
  1880. "temperature": (input_qwen_temperature, 'float'),
  1881. "history_enable": (switch_qwen_history_enable, 'bool'),
  1882. "history_max_len": (input_qwen_history_max_len, 'int'),
  1883. "preset": (input_qwen_preset, 'str'),
  1884. },
  1885. "chat_with_file": {
  1886. "chat_mode": (select_chat_with_file_chat_mode, 'str'),
  1887. "data_path": (input_chat_with_file_data_path, 'str'),
  1888. "separator": (input_chat_with_file_separator, 'str'),
  1889. "chunk_size": (input_chat_with_file_chunk_size, 'int'),
  1890. "chunk_overlap": (input_chat_with_file_chunk_overlap, 'int'),
  1891. "local_vector_embedding_model": (select_chat_with_file_local_vector_embedding_model, 'str'),
  1892. "chain_type": (input_chat_with_file_chain_type, 'str'),
  1893. "question_prompt": (input_chat_with_file_question_prompt, 'str'),
  1894. "local_max_query": (input_chat_with_file_local_max_query, 'int'),
  1895. "show_token_cost": (switch_chat_with_file_show_token_cost, 'bool'),
  1896. },
  1897. "chatterbot": {
  1898. "name": (input_chatterbot_name, 'str'),
  1899. "db_path": (input_chatterbot_db_path, 'str'),
  1900. },
  1901. "text_generation_webui": {
  1902. "type": (select_text_generation_webui_type, 'str'),
  1903. "api_ip_port": (input_text_generation_webui_api_ip_port, 'str'),
  1904. "max_new_tokens": (input_text_generation_webui_max_new_tokens, 'int'),
  1905. "history_enable": (switch_text_generation_webui_history_enable, 'bool'),
  1906. "history_max_len": (input_text_generation_webui_history_max_len, 'int'),
  1907. "mode": (select_text_generation_webui_mode, 'str'),
  1908. "character": (input_text_generation_webui_character, 'str'),
  1909. "instruction_template": (input_text_generation_webui_instruction_template, 'str'),
  1910. "your_name": (input_text_generation_webui_your_name, 'str'),
  1911. "top_p": (input_text_generation_webui_top_p, 'float'),
  1912. "top_k": (input_text_generation_webui_top_k, 'int'),
  1913. "temperature": (input_text_generation_webui_temperature, 'float'),
  1914. "seed": (input_text_generation_webui_seed, 'float'),
  1915. },
  1916. "sparkdesk": {
  1917. "type": (select_sparkdesk_type, 'str'),
  1918. "cookie": (input_sparkdesk_cookie, 'str'),
  1919. "fd": (input_sparkdesk_fd, 'str'),
  1920. "GtToken": (input_sparkdesk_GtToken, 'str'),
  1921. "app_id": (input_sparkdesk_app_id, 'str'),
  1922. "api_secret": (input_sparkdesk_api_secret, 'str'),
  1923. "api_key": (input_sparkdesk_api_key, 'str'),
  1924. "version": (select_sparkdesk_version, 'float'),
  1925. "assistant_id": (input_sparkdesk_assistant_id, 'str'),
  1926. },
  1927. "langchain_chatglm": {
  1928. "api_ip_port": (input_langchain_chatglm_api_ip_port, 'str'),
  1929. "chat_type": (select_langchain_chatglm_chat_type, 'str'),
  1930. "knowledge_base_id": (input_langchain_chatglm_knowledge_base_id, 'str'),
  1931. "history_enable": (switch_langchain_chatglm_history_enable, 'bool'),
  1932. "history_max_len": (input_langchain_chatglm_history_max_len, 'int'),
  1933. },
  1934. "langchain_chatchat": {
  1935. "api_ip_port": (input_langchain_chatchat_api_ip_port, 'str'),
  1936. "chat_type": (select_langchain_chatchat_chat_type, 'str'),
  1937. "history_enable": (switch_langchain_chatchat_history_enable, 'bool'),
  1938. "history_max_len": (input_langchain_chatchat_history_max_len, 'int'),
  1939. "llm": {
  1940. "model_name": (input_langchain_chatchat_llm_model_name, 'str'),
  1941. "temperature": (input_langchain_chatchat_llm_temperature, 'float'),
  1942. "max_tokens": (input_langchain_chatchat_llm_max_tokens, 'int'),
  1943. "prompt_name": (input_langchain_chatchat_llm_prompt_name, 'str'),
  1944. },
  1945. "knowledge_base": {
  1946. "knowledge_base_name": (input_langchain_chatchat_knowledge_base_knowledge_base_name, 'str'),
  1947. "top_k": (input_langchain_chatchat_knowledge_base_top_k, 'int'),
  1948. "score_threshold": (input_langchain_chatchat_knowledge_base_score_threshold, 'float'),
  1949. "model_name": (input_langchain_chatchat_knowledge_base_model_name, 'str'),
  1950. "temperature": (input_langchain_chatchat_knowledge_base_temperature, 'float'),
  1951. "max_tokens": (input_langchain_chatchat_knowledge_base_max_tokens, 'int'),
  1952. "prompt_name": (input_langchain_chatchat_knowledge_base_prompt_name, 'str'),
  1953. },
  1954. "search_engine": {
  1955. "search_engine_name": (select_langchain_chatchat_search_engine_search_engine_name, 'str'),
  1956. "top_k": (input_langchain_chatchat_search_engine_top_k, 'int'),
  1957. "model_name": (input_langchain_chatchat_search_engine_model_name, 'str'),
  1958. "temperature": (input_langchain_chatchat_search_engine_temperature, 'float'),
  1959. "max_tokens": (input_langchain_chatchat_search_engine_max_tokens, 'int'),
  1960. "prompt_name": (input_langchain_chatchat_search_engine_prompt_name, 'str'),
  1961. },
  1962. },
  1963. "zhipu": {
  1964. "api_key": (input_zhipu_api_key, 'str'),
  1965. "model": (select_zhipu_model, 'str'),
  1966. "app_id": (input_zhipu_app_id, 'str'),
  1967. "top_p": (input_zhipu_top_p, 'str'),
  1968. "temperature": (input_zhipu_temperature, 'str'),
  1969. "history_enable": (switch_zhipu_history_enable, 'bool'),
  1970. "history_max_len": (input_zhipu_history_max_len, 'str'),
  1971. "user_info": (input_zhipu_user_info, 'str'),
  1972. "bot_info": (input_zhipu_bot_info, 'str'),
  1973. "bot_name": (input_zhipu_bot_name, 'str'),
  1974. "username": (input_zhipu_username, 'str'),
  1975. "remove_useless": (switch_zhipu_remove_useless, 'bool'),
  1976. "stream": (switch_zhipu_stream, 'bool'),
  1977. "assistant_api": {
  1978. "api_key": (input_zhipu_assistant_api_api_key, 'str'),
  1979. "api_secret": (input_zhipu_assistant_api_api_secret, 'str'),
  1980. "assistant_id": (input_zhipu_assistant_api_assistant_id, 'str'),
  1981. },
  1982. },
  1983. "bard": {
  1984. "token": (input_bard_token, 'str'),
  1985. },
  1986. "tongyi": {
  1987. "type": (select_tongyi_type, 'str'),
  1988. "cookie_path": (input_tongyi_cookie_path, 'str'),
  1989. "api_key": (input_tongyi_api_key, 'str'),
  1990. "model": (select_tongyi_model, 'str'),
  1991. "preset": (input_tongyi_preset, 'str'),
  1992. "temperature": (input_tongyi_temperature, 'float'),
  1993. "top_p": (input_tongyi_top_p, 'float'),
  1994. "top_k": (input_tongyi_top_k, 'int'),
  1995. "enable_search": (switch_tongyi_enable_search, 'bool'),
  1996. "history_enable": (switch_tongyi_history_enable, 'bool'),
  1997. "history_max_len": (input_tongyi_history_max_len, 'int'),
  1998. "stream": (switch_tongyi_stream, 'bool'),
  1999. },
  2000. "tongyixingchen": {
  2001. "access_token": (input_tongyixingchen_access_token, 'str'),
  2002. "type": (select_tongyixingchen_type, 'str'),
  2003. "history_enable": (switch_tongyixingchen_history_enable, 'bool'),
  2004. "history_max_len": (input_tongyixingchen_history_max_len, 'int'),
  2005. "stream": (switch_tongyixingchen_stream, 'bool'),
  2006. "固定角色": {
  2007. "character_id": (input_tongyixingchen_GDJS_character_id, 'str'),
  2008. "top_p": (input_tongyixingchen_GDJS_top_p, 'float'),
  2009. "temperature": (input_tongyixingchen_GDJS_temperature, 'float'),
  2010. "seed": (input_tongyixingchen_GDJS_seed, 'int'),
  2011. "user_id": (input_tongyixingchen_GDJS_user_id, 'str'),
  2012. "username": (input_tongyixingchen_GDJS_username, 'str'),
  2013. "role_name": (input_tongyixingchen_GDJS_role_name, 'str'),
  2014. },
  2015. },
  2016. "my_wenxinworkshop": {
  2017. "type": (select_my_wenxinworkshop_type, 'str'),
  2018. "model": (select_my_wenxinworkshop_model, 'str'),
  2019. "api_key": (input_my_wenxinworkshop_api_key, 'str'),
  2020. "secret_key": (input_my_wenxinworkshop_secret_key, 'str'),
  2021. "top_p": (input_my_wenxinworkshop_top_p, 'float'),
  2022. "temperature": (input_my_wenxinworkshop_temperature, 'float'),
  2023. "penalty_score": (input_my_wenxinworkshop_penalty_score, 'float'),
  2024. "history_enable": (switch_my_wenxinworkshop_history_enable, 'bool'),
  2025. "history_max_len": (input_my_wenxinworkshop_history_max_len, 'int'),
  2026. "stream": (switch_my_wenxinworkshop_stream, 'bool'),
  2027. "app_id": (input_my_wenxinworkshop_app_id, 'str'),
  2028. "app_token": (input_my_wenxinworkshop_app_token, 'str'),
  2029. },
  2030. "gemini": {
  2031. "api_key": (input_gemini_api_key, 'str'),
  2032. "model": (select_gemini_model, 'str'),
  2033. "history_enable": (switch_gemini_history_enable, 'bool'),
  2034. "history_max_len": (input_gemini_history_max_len, 'int'),
  2035. "http_proxy": (input_gemini_http_proxy, 'str'),
  2036. "https_proxy": (input_gemini_https_proxy, 'str'),
  2037. "max_output_tokens": (input_gemini_max_output_tokens, 'int'),
  2038. "temperature": (input_gemini_max_temperature, 'float'),
  2039. "top_p": (input_gemini_top_p, 'float'),
  2040. "top_k": (input_gemini_top_k, 'int'),
  2041. },
  2042. "qanything": {
  2043. "type": (select_qanything_type, 'str'),
  2044. "app_key": (input_qanything_app_key, 'str'),
  2045. "app_secret": (input_qanything_app_secret, 'str'),
  2046. "api_ip_port": (input_qanything_api_ip_port, 'str'),
  2047. "user_id": (input_qanything_user_id, 'str'),
  2048. "kb_ids": (textarea_qanything_kb_ids, 'textarea'),
  2049. "history_enable": (switch_qanything_history_enable, 'bool'),
  2050. "history_max_len": (input_qanything_history_max_len, 'int'),
  2051. },
  2052. "koboldcpp": {
  2053. "api_ip_port": (input_koboldcpp_api_ip_port, 'str'),
  2054. "max_context_length": (input_koboldcpp_max_context_length, 'int'),
  2055. "max_length": (input_koboldcpp_max_length, 'int'),
  2056. "quiet": (switch_koboldcpp_quiet, 'bool'),
  2057. "rep_pen": (input_koboldcpp_rep_pen, 'float'),
  2058. "rep_pen_range": (input_koboldcpp_rep_pen_range, 'int'),
  2059. "rep_pen_slope": (input_koboldcpp_rep_pen_slope, 'int'),
  2060. "temperature": (input_koboldcpp_temperature, 'float'),
  2061. "tfs": (input_koboldcpp_tfs, 'int'),
  2062. "top_a": (input_koboldcpp_top_a, 'int'),
  2063. "top_p": (input_koboldcpp_top_p, 'float'),
  2064. "top_k": (input_koboldcpp_top_k, 'int'),
  2065. "typical": (input_koboldcpp_typical, 'int'),
  2066. "history_enable": (switch_koboldcpp_history_enable, 'bool'),
  2067. "history_max_len": (input_koboldcpp_history_max_len, 'int'),
  2068. },
  2069. "anythingllm": {
  2070. "api_ip_port": (input_anythingllm_api_ip_port, 'str'),
  2071. "api_key": (input_anythingllm_api_key, 'str'),
  2072. "mode": (select_anythingllm_mode, 'str'),
  2073. "workspace_slug": (select_anythingllm_workspace_slug, 'str'),
  2074. },
  2075. "dify": {
  2076. "api_ip_port": (input_dify_api_ip_port, 'str'),
  2077. "api_key": (input_dify_api_key, 'str'),
  2078. "type": (select_dify_type, 'str'),
  2079. "history_enable": (switch_dify_history_enable, 'bool'),
  2080. },
  2081. "gpt4free": {
  2082. "provider": (select_gpt4free_provider, 'str'),
  2083. "api_key": (input_gpt4free_api_key, 'str'),
  2084. "model": (select_gpt4free_model, 'str'),
  2085. "proxy": (input_gpt4free_proxy, 'str'),
  2086. "max_tokens": (input_gpt4free_max_tokens, 'int'),
  2087. "preset": (input_gpt4free_preset, 'str'),
  2088. "history_enable": (switch_gpt4free_history_enable, 'bool'),
  2089. "history_max_len": (input_gpt4free_history_max_len, 'int'),
  2090. },
  2091. "volcengine": {
  2092. "api_key": (input_volcengine_api_key, 'str'),
  2093. "model": (input_volcengine_model, 'str'),
  2094. "preset": (input_volcengine_preset, 'str'),
  2095. "history_enable": (switch_volcengine_history_enable, 'bool'),
  2096. "history_max_len": (input_volcengine_history_max_len, 'int'),
  2097. "stream": (switch_volcengine_stream, 'bool'),
  2098. },
  2099. "custom_llm": {
  2100. "url": (textarea_custom_llm_url, 'str'),
  2101. "method": (textarea_custom_llm_method, 'str'),
  2102. "headers": (textarea_custom_llm_headers, 'str'),
  2103. "proxies": (textarea_custom_llm_proxies, 'str'),
  2104. "body_type": (select_custom_llm_body_type, 'str'),
  2105. "body": (textarea_custom_llm_body, 'str'),
  2106. "resp_data_type": (select_custom_llm_resp_data_type, 'str'),
  2107. "data_analysis": (textarea_custom_llm_data_analysis, 'str'),
  2108. "resp_template": (textarea_custom_llm_resp_template, 'str'),
  2109. },
  2110. "llm_tpu": {
  2111. "api_ip_port": (input_llm_tpu_api_ip_port, 'str'),
  2112. "history_enable": (switch_llm_tpu_history_enable, 'bool'),
  2113. "history_max_len": (input_llm_tpu_history_max_len, 'int'),
  2114. "max_length": (input_llm_tpu_max_length, 'float'),
  2115. "temperature": (input_llm_tpu_temperature, 'float'),
  2116. "top_p": (input_llm_tpu_top_p, 'float'),
  2117. },
  2118. }
  2119. config_data = update_config(config_mapping, config, config_data, "llm")
  2120. if config.get("webui", "show_card", "llm", "claude"):
  2121. config_data["claude2"]["cookie"] = input_claude2_cookie.value
  2122. config_data["claude2"]["use_proxy"] = switch_claude2_use_proxy.value
  2123. config_data["claude2"]["proxies"]["http"] = input_claude2_proxies_http.value
  2124. config_data["claude2"]["proxies"]["https"] = input_claude2_proxies_https.value
  2125. config_data["claude2"]["proxies"]["socks5"] = input_claude2_proxies_socks5.value
  2126. """
  2127. TTS
  2128. """
  2129. if True:
  2130. config_mapping = {
  2131. "edge-tts": {
  2132. "voice": (select_edge_tts_voice, 'str'),
  2133. "rate": (input_edge_tts_rate, 'str'),
  2134. "volume": (input_edge_tts_volume, 'str'),
  2135. },
  2136. "vits": {
  2137. "type": (select_vits_type, 'str'),
  2138. "config_path": (input_vits_config_path, 'str'),
  2139. "api_ip_port": (input_vits_api_ip_port, 'str'),
  2140. "id": (select_vits_id, 'str'),
  2141. "lang": (select_vits_lang, 'str'),
  2142. "length": (input_vits_length, 'str'),
  2143. "noise": (input_vits_noise, 'str'),
  2144. "noisew": (input_vits_noisew, 'str'),
  2145. "max": (input_vits_max, 'str'),
  2146. "format": (input_vits_format, 'str'),
  2147. "sdp_radio": (input_vits_sdp_radio, 'str'),
  2148. "gpt_sovits": {
  2149. "id": (select_vits_gpt_sovits_id, 'str'),
  2150. "lang": (select_vits_gpt_sovits_lang, 'str'),
  2151. "format": (input_vits_gpt_sovits_format, 'str'),
  2152. "segment_size": (input_vits_gpt_sovits_segment_size, 'str'),
  2153. "reference_audio": (input_vits_gpt_sovits_reference_audio, 'str'),
  2154. "prompt_text": (input_vits_gpt_sovits_prompt_text, 'str'),
  2155. "prompt_lang": (select_vits_gpt_sovits_prompt_lang, 'str'),
  2156. "top_k": (input_vits_gpt_sovits_top_k, 'str'),
  2157. "top_p": (input_vits_gpt_sovits_top_p, 'str'),
  2158. "temperature": (input_vits_gpt_sovits_temperature, 'str'),
  2159. "preset": (input_vits_gpt_sovits_preset, 'str'),
  2160. },
  2161. },
  2162. "bert_vits2": {
  2163. "type": (select_bert_vits2_type, 'str'),
  2164. "api_ip_port": (input_bert_vits2_api_ip_port, 'str'),
  2165. "model_id": (input_bert_vits2_model_id, 'int'),
  2166. "speaker_name": (input_bert_vits2_speaker_name, 'str'),
  2167. "speaker_id": (input_bert_vits2_speaker_id, 'int'),
  2168. "language": (select_bert_vits2_language, 'str'),
  2169. "length": (input_bert_vits2_length, 'int'),
  2170. "noise": (input_bert_vits2_noise, 'float'),
  2171. "noisew": (input_bert_vits2_noisew, 'float'),
  2172. "sdp_radio": (input_bert_vits2_sdp_radio, 'float'),
  2173. "emotion": (input_bert_vits2_emotion, 'str'),
  2174. "style_text": (input_bert_vits2_style_text, 'str'),
  2175. "style_weight": (input_bert_vits2_style_weight, 'float'),
  2176. "auto_translate": (switch_bert_vits2_auto_translate, 'bool'),
  2177. "auto_split": (switch_bert_vits2_auto_split, 'bool'),
  2178. "刘悦-中文特化API": {
  2179. "api_ip_port": (input_bert_vits2_liuyue_zh_api_api_ip_port, 'str'),
  2180. "speaker": (input_bert_vits2_liuyue_zh_api_speaker, 'str'),
  2181. "language": (select_bert_vits2_liuyue_zh_api_language, 'str'),
  2182. "length_scale": (input_bert_vits2_liuyue_zh_api_length_scale, 'str'),
  2183. "interval_between_para": (input_bert_vits2_liuyue_zh_api_interval_between_para, 'str'),
  2184. "interval_between_sent": (input_bert_vits2_liuyue_zh_api_interval_between_sent, 'str'),
  2185. "noise_scale": (input_bert_vits2_liuyue_zh_api_noise_scale, 'str'),
  2186. "noise_scale_w": (input_bert_vits2_liuyue_zh_api_noise_scale_w, 'str'),
  2187. "sdp_radio": (input_bert_vits2_liuyue_zh_api_sdp_radio, 'str'),
  2188. "emotion": (input_bert_vits2_liuyue_zh_api_emotion, 'str'),
  2189. "style_text": (input_bert_vits2_liuyue_zh_api_style_text, 'str'),
  2190. "style_weight": (input_bert_vits2_liuyue_zh_api_style_weight, 'str'),
  2191. "cut_by_sent": (switch_bert_vits2_cut_by_sent, 'bool'),
  2192. },
  2193. },
  2194. "vits_fast": {
  2195. "config_path": (input_vits_fast_config_path, 'str'),
  2196. "api_ip_port": (input_vits_fast_api_ip_port, 'str'),
  2197. "character": (input_vits_fast_character, 'str'),
  2198. "language": (select_vits_fast_language, 'str'),
  2199. "speed": (input_vits_fast_speed, 'float'),
  2200. },
  2201. "elevenlabs": {
  2202. "api_key": (input_elevenlabs_api_key, 'str'),
  2203. "voice": (input_elevenlabs_voice, 'str'),
  2204. "model": (input_elevenlabs_model, 'str'),
  2205. },
  2206. "bark_gui": {
  2207. "api_ip_port": (input_bark_gui_api_ip_port, 'str'),
  2208. "spk": (input_bark_gui_spk, 'str'),
  2209. "generation_temperature": (input_bark_gui_generation_temperature, 'float'),
  2210. "waveform_temperature": (input_bark_gui_waveform_temperature, 'float'),
  2211. "end_of_sentence_probability": (input_bark_gui_end_of_sentence_probability, 'float'),
  2212. "quick_generation": (switch_bark_gui_quick_generation, 'bool'),
  2213. "seed": (input_bark_gui_seed, 'float'),
  2214. "batch_count": (input_bark_gui_batch_count, 'int'),
  2215. },
  2216. "vall_e_x": {
  2217. "api_ip_port": (input_vall_e_x_api_ip_port, 'str'),
  2218. "language": (select_vall_e_x_language, 'str'),
  2219. "accent": (select_vall_e_x_accent, 'str'),
  2220. "voice_preset": (input_vall_e_x_voice_preset, 'str'),
  2221. "voice_preset_file_path": (input_vall_e_x_voice_preset_file_path, 'str'),
  2222. },
  2223. "openai_tts": {
  2224. "type": (select_openai_tts_type, 'str'),
  2225. "api_ip_port": (input_openai_tts_api_ip_port, 'str'),
  2226. "model": (select_openai_tts_model, 'str'),
  2227. "voice": (select_openai_tts_voice, 'str'),
  2228. "api_key": (input_openai_tts_api_key, 'str'),
  2229. },
  2230. "reecho_ai": {
  2231. "Authorization": (input_reecho_ai_Authorization, 'str'),
  2232. "model": (input_reecho_ai_model, 'str'),
  2233. "voiceId": (input_reecho_ai_voiceId, 'str'),
  2234. "randomness": (number_reecho_ai_randomness, 'int'),
  2235. "stability_boost": (number_reecho_ai_stability_boost, 'int'),
  2236. "promptId": (input_reecho_ai_promptId, 'str'),
  2237. "probability_optimization": (number_reecho_ai_probability_optimization, 'int'),
  2238. "break_clone": (switch_reecho_ai_break_clone, 'bool'),
  2239. "flash": (switch_reecho_ai_flash, 'bool'),
  2240. },
  2241. "gradio_tts": {
  2242. "request_parameters": (textarea_gradio_tts_request_parameters, 'str'),
  2243. },
  2244. "gpt_sovits": {
  2245. "type": (select_gpt_sovits_type, 'str'),
  2246. "gradio_ip_port": (input_gpt_sovits_gradio_ip_port, 'str'),
  2247. "api_ip_port": (input_gpt_sovits_api_ip_port, 'str'),
  2248. "ws_ip_port": (input_gpt_sovits_ws_ip_port, 'str'),
  2249. "ref_audio_path": (input_gpt_sovits_ref_audio_path, 'str'),
  2250. "prompt_text": (input_gpt_sovits_prompt_text, 'str'),
  2251. "prompt_language": (select_gpt_sovits_prompt_language, 'str'),
  2252. "language": (select_gpt_sovits_language, 'str'),
  2253. "cut": (select_gpt_sovits_cut, 'str'),
  2254. "gpt_model_path": (input_gpt_sovits_gpt_model_path, 'str'),
  2255. "sovits_model_path": (input_gpt_sovits_sovits_model_path, 'str'),
  2256. "api_0322": {
  2257. "ref_audio_path": (input_gpt_sovits_api_0322_ref_audio_path, 'str'),
  2258. "prompt_text": (input_gpt_sovits_api_0322_prompt_text, 'str'),
  2259. "prompt_lang": (select_gpt_sovits_api_0322_prompt_lang, 'str'),
  2260. "text_lang": (select_gpt_sovits_api_0322_text_lang, 'str'),
  2261. "text_split_method": (select_gpt_sovits_api_0322_text_split_method, 'str'),
  2262. "top_k": (input_gpt_sovits_api_0322_top_k, 'int'),
  2263. "top_p": (input_gpt_sovits_api_0322_top_p, 'float'),
  2264. "temperature": (input_gpt_sovits_api_0322_temperature, 'float'),
  2265. "batch_size": (input_gpt_sovits_api_0322_batch_size, 'int'),
  2266. "speed_factor": (input_gpt_sovits_api_0322_speed_factor, 'float'),
  2267. "fragment_interval": (input_gpt_sovits_api_0322_fragment_interval, 'str'),
  2268. "split_bucket": (switch_gpt_sovits_api_0322_split_bucket, 'bool'),
  2269. "return_fragment": (switch_gpt_sovits_api_0322_return_fragment, 'bool'),
  2270. },
  2271. "api_0706": {
  2272. "refer_wav_path": (input_gpt_sovits_api_0706_refer_wav_path, 'str'),
  2273. "prompt_text": (input_gpt_sovits_api_0706_prompt_text, 'str'),
  2274. "prompt_language": (select_gpt_sovits_api_0706_prompt_language, 'str'),
  2275. "text_language": (select_gpt_sovits_api_0706_text_language, 'str'),
  2276. "cut_punc": (input_gpt_sovits_api_0706_cut_punc, 'str'),
  2277. },
  2278. "v2_api_0821": {
  2279. "ref_audio_path": (input_gpt_sovits_v2_api_0821_ref_audio_path, 'str'),
  2280. "prompt_text": (input_gpt_sovits_v2_api_0821_prompt_text, 'str'),
  2281. "prompt_lang": (select_gpt_sovits_v2_api_0821_prompt_lang, 'str'),
  2282. "text_lang": (select_gpt_sovits_v2_api_0821_text_lang, 'str'),
  2283. "text_split_method": (select_gpt_sovits_v2_api_0821_text_split_method, 'str'),
  2284. "top_k": (input_gpt_sovits_v2_api_0821_top_k, 'int'),
  2285. "top_p": (input_gpt_sovits_v2_api_0821_top_p, 'float'),
  2286. "temperature": (input_gpt_sovits_v2_api_0821_temperature, 'float'),
  2287. "batch_size": (input_gpt_sovits_v2_api_0821_batch_size, 'int'),
  2288. "batch_threshold": (input_gpt_sovits_v2_api_0821_batch_threshold, 'float'),
  2289. "split_bucket": (switch_gpt_sovits_v2_api_0821_split_bucket, 'bool'),
  2290. "speed_factor": (input_gpt_sovits_v2_api_0821_speed_factor, 'float'),
  2291. "fragment_interval": (input_gpt_sovits_v2_api_0821_fragment_interval, 'float'),
  2292. "seed": (input_gpt_sovits_v2_api_0821_seed, 'int'),
  2293. "media_type": (input_gpt_sovits_v2_api_0821_media_type, 'str'),
  2294. "parallel_infer": (switch_gpt_sovits_v2_api_0821_parallel_infer, 'bool'),
  2295. "repetition_penalty": (input_gpt_sovits_v2_api_0821_repetition_penalty, 'float'),
  2296. },
  2297. "webtts": {
  2298. "version": (select_gpt_sovits_webtts_version, 'str'),
  2299. "api_ip_port": (input_gpt_sovits_webtts_api_ip_port, 'str'),
  2300. "spk": (input_gpt_sovits_webtts_spk, 'str'),
  2301. "lang": (select_gpt_sovits_webtts_lang, 'str'),
  2302. "speed": (input_gpt_sovits_webtts_speed, 'str'),
  2303. "emotion": (input_gpt_sovits_webtts_emotion, 'str'),
  2304. },
  2305. },
  2306. "clone_voice": {
  2307. "type": (select_clone_voice_type, 'str'),
  2308. "api_ip_port": (input_clone_voice_api_ip_port, 'str'),
  2309. "voice": (input_clone_voice_voice, 'str'),
  2310. "language": (select_clone_voice_language, 'str'),
  2311. "speed": (input_clone_voice_speed, 'float'),
  2312. },
  2313. "azure_tts": {
  2314. "subscription_key": (input_azure_tts_subscription_key, 'str'),
  2315. "region": (input_azure_tts_region, 'str'),
  2316. "voice_name": (input_azure_tts_voice_name, 'str'),
  2317. },
  2318. "fish_speech": {
  2319. "type": (select_fish_speech_type, 'str'),
  2320. "api_ip_port": (input_fish_speech_api_ip_port, 'str'),
  2321. "model_name": (input_fish_speech_model_name, 'str'),
  2322. "model_config": {
  2323. "device": (input_fish_speech_model_config_device, 'str'),
  2324. "llama": {
  2325. "config_name": (input_fish_speech_model_config_llama_config_name, 'str'),
  2326. "checkpoint_path": (input_fish_speech_model_config_llama_checkpoint_path, 'str'),
  2327. "precision": (input_fish_speech_model_config_llama_precision, 'str'),
  2328. "tokenizer": (input_fish_speech_model_config_llama_tokenizer, 'str'),
  2329. "compile": (switch_fish_speech_model_config_llama_compile, 'bool'),
  2330. },
  2331. "vqgan": {
  2332. "config_name": (input_fish_speech_model_config_vqgan_config_name, 'str'),
  2333. "checkpoint_path": (input_fish_speech_model_config_vqgan_checkpoint_path, 'str'),
  2334. },
  2335. },
  2336. "tts_config": {
  2337. "prompt_text": (input_fish_speech_tts_config_prompt_text, 'str'),
  2338. "prompt_tokens": (input_fish_speech_tts_config_prompt_tokens, 'str'),
  2339. "max_new_tokens": (input_fish_speech_tts_config_max_new_tokens, 'int'),
  2340. "top_k": (input_fish_speech_tts_config_top_k, 'int'),
  2341. "top_p": (input_fish_speech_tts_config_top_p, 'float'),
  2342. "repetition_penalty": (input_fish_speech_tts_config_repetition_penalty, 'float'),
  2343. "temperature": (input_fish_speech_tts_config_temperature, 'float'),
  2344. "order": (input_fish_speech_tts_config_order, 'str'),
  2345. "seed": (input_fish_speech_tts_config_seed, 'int'),
  2346. "speaker": (input_fish_speech_tts_config_speaker, 'str'),
  2347. "use_g2p": (switch_fish_speech_tts_config_use_g2p, 'bool'),
  2348. },
  2349. "api_1.1.0": {
  2350. "reference_text": (input_fish_speech_api_1_1_0_reference_text, 'str'),
  2351. "reference_audio": (input_fish_speech_api_1_1_0_reference_audio, 'str'),
  2352. "max_new_tokens": (input_fish_speech_api_1_1_0_max_new_tokens, 'int'),
  2353. "chunk_length": (input_fish_speech_api_1_1_0_chunk_length, 'int'),
  2354. "top_p": (input_fish_speech_api_1_1_0_top_p, 'float'),
  2355. "repetition_penalty": (input_fish_speech_api_1_1_0_repetition_penalty, 'float'),
  2356. "temperature": (input_fish_speech_api_1_1_0_temperature, 'float'),
  2357. "speaker": (input_fish_speech_api_1_1_0_speaker, 'str'),
  2358. "format": (input_fish_speech_api_1_1_0_format, 'str'),
  2359. },
  2360. "web": {
  2361. "speaker": (input_fish_speech_web_speaker, 'str'),
  2362. "enable_ref_audio": (switch_fish_speech_web_enable_ref_audio, 'bool'),
  2363. "ref_audio_path": (input_fish_speech_web_ref_audio_path, 'str'),
  2364. "ref_text": (input_fish_speech_web_ref_text, 'str'),
  2365. "enable_ref_audio_update": (switch_fish_speech_enable_ref_audio_update, 'bool'),
  2366. "maximum_tokens_per_batch": (input_fish_speech_web_maximum_tokens_per_batch, 'int'),
  2367. "iterative_prompt_length": (input_fish_speech_web_iterative_prompt_length, 'int'),
  2368. "temperature": (input_fish_speech_web_temperature, 'float'),
  2369. "top_p": (input_fish_speech_web_top_p, 'float'),
  2370. "repetition_penalty": (input_fish_speech_web_repetition_penalty, 'float'),
  2371. },
  2372. },
  2373. "chattts": {
  2374. "type": (select_chattts_type, 'str'),
  2375. "api_ip_port": (input_chattts_api_ip_port, 'str'),
  2376. "gradio_ip_port": (input_chattts_gradio_ip_port, 'str'),
  2377. "temperature": (input_chattts_temperature, 'float'),
  2378. "audio_seed_input": (input_chattts_audio_seed_input, 'int'),
  2379. "top_p": (input_chattts_top_p, 'float'),
  2380. "top_k": (input_chattts_top_k, 'int'),
  2381. "text_seed_input": (input_chattts_text_seed_input, 'int'),
  2382. "refine_text_flag": (switch_chattts_refine_text_flag, 'bool'),
  2383. "api": {
  2384. "seed": (input_chattts_api_seed, 'int'),
  2385. "media_type": (input_chattts_api_media_type, 'str'),
  2386. },
  2387. },
  2388. "cosyvoice": {
  2389. "type": (select_cosyvoice_type, 'str'),
  2390. "gradio_ip_port": (input_cosyvoice_gradio_ip_port, 'str'),
  2391. "api_ip_port": (input_cosyvoice_api_ip_port, 'str'),
  2392. "gradio_0707": {
  2393. "mode_checkbox_group": (select_cosyvoice_gradio_0707_mode_checkbox_group, 'str'),
  2394. "sft_dropdown": (select_cosyvoice_gradio_0707_sft_dropdown, 'str'),
  2395. "prompt_text": (input_cosyvoice_gradio_0707_prompt_text, 'str'),
  2396. "prompt_wav_upload": (input_cosyvoice_gradio_0707_prompt_wav_upload, 'str'),
  2397. "instruct_text": (input_cosyvoice_gradio_0707_instruct_text, 'str'),
  2398. "seed": (input_cosyvoice_gradio_0707_seed, 'int'),
  2399. },
  2400. "api_0819": {
  2401. "speaker": (input_cosyvoice_api_0819_speaker, 'str'),
  2402. "new": (input_cosyvoice_api_0819_new, 'int'),
  2403. "speed": (input_cosyvoice_api_0819_speed, 'float'),
  2404. },
  2405. },
  2406. }
  2407. config_data = update_config(config_mapping, config, config_data, "tts")
  2408. """
  2409. SVC
  2410. """
  2411. if True:
  2412. config_mapping = {
  2413. "ddsp_svc": {
  2414. "enable": (switch_ddsp_svc_enable, 'bool'),
  2415. "config_path": (input_ddsp_svc_config_path, 'str'),
  2416. "api_ip_port": (input_ddsp_svc_api_ip_port, 'str'),
  2417. "fSafePrefixPadLength": (input_ddsp_svc_fSafePrefixPadLength, 'float'),
  2418. "fPitchChange": (input_ddsp_svc_fPitchChange, 'float'),
  2419. "sSpeakId": (input_ddsp_svc_sSpeakId, 'int'),
  2420. "sampleRate": (input_ddsp_svc_sampleRate, 'int'),
  2421. },
  2422. "so_vits_svc": {
  2423. "enable": (switch_so_vits_svc_enable, 'bool'),
  2424. "config_path": (input_so_vits_svc_config_path, 'str'),
  2425. "api_ip_port": (input_so_vits_svc_api_ip_port, 'str'),
  2426. "spk": (input_so_vits_svc_spk, 'str'),
  2427. "tran": (input_so_vits_svc_tran, 'float'),
  2428. "wav_format": (input_so_vits_svc_wav_format, 'str'),
  2429. },
  2430. }
  2431. config_data = update_config(config_mapping, config, config_data, "svc")
  2432. """
  2433. 虚拟身体
  2434. """
  2435. if True:
  2436. config_mapping = {
  2437. "live2d": {
  2438. "enable": (switch_live2d_enable, 'bool'),
  2439. "port": (input_live2d_port, 'int'),
  2440. "name": (select_live2d_name, 'str'),
  2441. },
  2442. "live2d_TTS_LLM_GPT_SoVITS_Vtuber": {
  2443. "api_ip_port": (input_live2d_TTS_LLM_GPT_SoVITS_Vtuber_api_ip_port, 'str'),
  2444. },
  2445. "xuniren": {
  2446. "api_ip_port": (input_xuniren_api_ip_port, 'str'),
  2447. },
  2448. "metahuman_stream": {
  2449. "type": (select_metahuman_stream_type, 'str'),
  2450. "api_ip_port": (input_metahuman_stream_api_ip_port, 'str'),
  2451. },
  2452. "unity": {
  2453. # "enable": (switch_unity_enable, 'bool'),
  2454. "api_ip_port": (input_unity_api_ip_port, 'str'),
  2455. "password": (input_unity_password, 'str'),
  2456. },
  2457. "EasyAIVtuber": {
  2458. "api_ip_port": (input_EasyAIVtuber_api_ip_port, 'str'),
  2459. },
  2460. "digital_human_video_player": {
  2461. "type": (select_digital_human_video_player_type, 'str'),
  2462. "api_ip_port": (input_digital_human_video_player_api_ip_port, 'str'),
  2463. }
  2464. }
  2465. config_data = update_config(config_mapping, config, config_data, None)
  2466. if config.get("webui", "show_card", "visual_body", "live2d"):
  2467. tmp_str = f"var model_name = \"{select_live2d_name.value}\";"
  2468. # 路径写死了,注意
  2469. common.write_content_to_file("Live2D/js/model_name.js", tmp_str)
  2470. """
  2471. 文案
  2472. """
  2473. if True:
  2474. config_data["copywriting"]["auto_play"] = switch_copywriting_auto_play.value
  2475. config_data["copywriting"]["random_play"] = switch_copywriting_random_play.value
  2476. config_data["copywriting"]["audio_interval"] = input_copywriting_audio_interval.value
  2477. config_data["copywriting"]["switching_interval"] = input_copywriting_switching_interval.value
  2478. config_data["copywriting"]["text_path"] = input_copywriting_text_path.value
  2479. config_data["copywriting"]["audio_save_path"] = input_copywriting_audio_save_path.value
  2480. config_data["copywriting"]["audio_synthesis_type"] = select_copywriting_audio_synthesis_type.value
  2481. tmp_arr = []
  2482. # logger.info(copywriting_config_var)
  2483. for index in range(len(copywriting_config_var) // 5):
  2484. tmp_json = {
  2485. "file_path": "",
  2486. "audio_path": "",
  2487. "continuous_play_num": 1,
  2488. "max_play_time": 10.0,
  2489. "play_list": []
  2490. }
  2491. tmp_json["file_path"] = copywriting_config_var[str(5 * index)].value
  2492. tmp_json["audio_path"] = copywriting_config_var[str(5 * index + 1)].value
  2493. tmp_json["continuous_play_num"] = int(copywriting_config_var[str(5 * index + 2)].value)
  2494. tmp_json["max_play_time"] = float(copywriting_config_var[str(5 * index + 3)].value)
  2495. tmp_json["play_list"] = common_textarea_handle(copywriting_config_var[str(5 * index + 4)].value)
  2496. tmp_arr.append(tmp_json)
  2497. # logger.info(tmp_arr)
  2498. config_data["copywriting"]["config"] = tmp_arr
  2499. """
  2500. 积分
  2501. """
  2502. if True:
  2503. config_data["integral"]["enable"] = switch_integral_enable.value
  2504. config_data["integral"]["sign"]["enable"] = switch_integral_sign_enable.value
  2505. config_data["integral"]["sign"]["get_integral"] = int(input_integral_sign_get_integral.value)
  2506. config_data["integral"]["sign"]["cmd"] = common_textarea_handle(textarea_integral_sign_cmd.value)
  2507. tmp_arr = []
  2508. # logger.info(integral_sign_copywriting_var)
  2509. for index in range(len(integral_sign_copywriting_var) // 2):
  2510. tmp_json = {
  2511. "sign_num_interval": "",
  2512. "copywriting": []
  2513. }
  2514. tmp_json["sign_num_interval"] = integral_sign_copywriting_var[str(2 * index)].value
  2515. tmp_json["copywriting"] = common_textarea_handle(integral_sign_copywriting_var[str(2 * index + 1)].value)
  2516. tmp_arr.append(tmp_json)
  2517. # logger.info(tmp_arr)
  2518. config_data["integral"]["sign"]["copywriting"] = tmp_arr
  2519. config_data["integral"]["gift"]["enable"] = switch_integral_gift_enable.value
  2520. config_data["integral"]["gift"]["get_integral_proportion"] = float(input_integral_gift_get_integral_proportion.value)
  2521. tmp_arr = []
  2522. for index in range(len(integral_gift_copywriting_var) // 2):
  2523. tmp_json = {
  2524. "gift_price_interval": "",
  2525. "copywriting": []
  2526. }
  2527. tmp_json["gift_price_interval"] = integral_gift_copywriting_var[str(2 * index)].value
  2528. tmp_json["copywriting"] = common_textarea_handle(integral_gift_copywriting_var[str(2 * index + 1)].value)
  2529. tmp_arr.append(tmp_json)
  2530. # logger.info(tmp_arr)
  2531. config_data["integral"]["gift"]["copywriting"] = tmp_arr
  2532. config_data["integral"]["entrance"]["enable"] = switch_integral_entrance_enable.value
  2533. config_data["integral"]["entrance"]["get_integral"] = int(input_integral_entrance_get_integral.value)
  2534. tmp_arr = []
  2535. for index in range(len(integral_entrance_copywriting_var) // 2):
  2536. tmp_json = {
  2537. "entrance_num_interval": "",
  2538. "copywriting": []
  2539. }
  2540. tmp_json["entrance_num_interval"] = integral_entrance_copywriting_var[str(2 * index)].value
  2541. tmp_json["copywriting"] = common_textarea_handle(integral_entrance_copywriting_var[str(2 * index + 1)].value)
  2542. tmp_arr.append(tmp_json)
  2543. # logger.info(tmp_arr)
  2544. config_data["integral"]["entrance"]["copywriting"] = tmp_arr
  2545. config_data["integral"]["crud"]["query"]["enable"] = switch_integral_crud_query_enable.value
  2546. config_data["integral"]["crud"]["query"]["cmd"] = common_textarea_handle(textarea_integral_crud_query_cmd.value)
  2547. config_data["integral"]["crud"]["query"]["copywriting"] = common_textarea_handle(textarea_integral_crud_query_copywriting.value)
  2548. """
  2549. 聊天
  2550. """
  2551. if True:
  2552. config_mapping = {
  2553. "talk": {
  2554. "key_listener_enable": (switch_talk_key_listener_enable, 'bool'),
  2555. "direct_run_talk": (switch_talk_direct_run_talk, 'bool'),
  2556. "device_index": (select_talk_device_index, 'str'),
  2557. "no_recording_during_playback": (switch_talk_no_recording_during_playback, 'bool'),
  2558. "no_recording_during_playback_sleep_interval": (input_talk_no_recording_during_playback_sleep_interval, 'float'),
  2559. "username": (input_talk_username, 'str'),
  2560. "continuous_talk": (switch_talk_continuous_talk, 'bool'),
  2561. "trigger_key": (select_talk_trigger_key, 'str'),
  2562. "stop_trigger_key": (select_talk_stop_trigger_key, 'str'),
  2563. "volume_threshold": (input_talk_volume_threshold, 'float'),
  2564. "silence_threshold": (input_talk_silence_threshold, 'float'),
  2565. "CHANNELS": (input_talk_silence_CHANNELS, 'int'),
  2566. "RATE": (input_talk_silence_RATE, 'int'),
  2567. "show_chat_log": (switch_talk_show_chat_log, 'bool'),
  2568. "wakeup_sleep": {
  2569. "enable": (switch_talk_wakeup_sleep_enable, 'bool'),
  2570. "mode": (select_talk_wakeup_sleep_mode, 'str'),
  2571. "wakeup_word": (textarea_talk_wakeup_sleep_wakeup_word, 'textarea'),
  2572. "sleep_word": (textarea_talk_wakeup_sleep_sleep_word, 'textarea'),
  2573. "wakeup_copywriting": (textarea_talk_wakeup_sleep_wakeup_copywriting, 'textarea'),
  2574. "sleep_copywriting": (textarea_talk_wakeup_sleep_sleep_copywriting, 'textarea'),
  2575. },
  2576. "type": (select_talk_type, 'str'),
  2577. "google": {
  2578. "tgt_lang": (select_talk_google_tgt_lang, 'str'),
  2579. },
  2580. "baidu": {
  2581. "app_id": (input_talk_baidu_app_id, 'str'),
  2582. "api_key": (input_talk_baidu_api_key, 'str'),
  2583. "secret_key": (input_talk_baidu_secret_key, 'str'),
  2584. },
  2585. "faster_whisper": {
  2586. "model_size": (input_faster_whisper_model_size, 'str'),
  2587. "language": (select_faster_whisper_language, 'str'),
  2588. "device": (select_faster_whisper_device, 'str'),
  2589. "compute_type": (select_faster_whisper_compute_type, 'str'),
  2590. "download_root": (input_faster_whisper_download_root, 'str'),
  2591. "beam_size": (input_faster_whisper_beam_size, 'int'),
  2592. },
  2593. "sensevoice": {
  2594. "asr_model_path": (input_sensevoice_asr_model_path, 'str'),
  2595. "vad_model_path": (input_sensevoice_vad_model_path, 'str'),
  2596. "vad_max_single_segment_time": (input_sensevoice_vad_max_single_segment_time, 'int'),
  2597. "device": (input_sensevoice_vad_device, 'str'),
  2598. "language": (select_sensevoice_language, 'str'),
  2599. "text_norm": (input_sensevoice_text_norm, 'str'),
  2600. "batch_size_s": (input_sensevoice_batch_size_s, 'int'),
  2601. "batch_size": (input_sensevoice_batch_size, 'int'),
  2602. },
  2603. }
  2604. }
  2605. config_data = update_config(config_mapping, config, config_data, None)
  2606. """
  2607. 图像识别
  2608. """
  2609. if True:
  2610. config_mapping = {
  2611. "image_recognition": {
  2612. "enable": (button_image_recognition_enable, 'bool'),
  2613. "model": (select_image_recognition_model, 'str'),
  2614. "img_save_path": (input_image_recognition_img_save_path, 'str'),
  2615. "prompt": (input_image_recognition_prompt, 'str'),
  2616. "screenshot_window_title": (select_image_recognition_screenshot_window_title, 'str'),
  2617. "screenshot_delay": (input_image_recognition_screenshot_delay, 'float'),
  2618. "loop_screenshot_enable": (switch_image_recognition_loop_screenshot_enable, 'bool'),
  2619. "loop_screenshot_delay": (input_image_recognition_loop_screenshot_delay, 'int'),
  2620. "cam_screenshot_enable": (switch_image_recognition_cam_screenshot_enable, 'bool'),
  2621. "cam_index": (select_image_recognition_cam_index, 'int'),
  2622. "cam_screenshot_delay": (input_image_recognition_cam_screenshot_delay, 'float'),
  2623. "loop_cam_screenshot_enable": (switch_image_recognition_loop_cam_screenshot_enable, 'bool'),
  2624. "loop_cam_screenshot_delay": (input_image_recognition_loop_cam_screenshot_delay, 'int'),
  2625. "gemini": {
  2626. "model": (select_image_recognition_gemini_model, 'str'),
  2627. "api_key": (input_image_recognition_gemini_api_key, 'str'),
  2628. "http_proxy": (input_image_recognition_gemini_http_proxy, 'str'),
  2629. "https_proxy": (input_image_recognition_gemini_https_proxy, 'str'),
  2630. },
  2631. "zhipu": {
  2632. "model": (select_image_recognition_zhipu_model, 'str'),
  2633. "api_key": (input_image_recognition_zhipu_api_key, 'str'),
  2634. },
  2635. "blip": {
  2636. "model": (select_image_recognition_blip_model, 'str'),
  2637. },
  2638. }
  2639. }
  2640. config_data = update_config(config_mapping, config, config_data, None)
  2641. """
  2642. 助播
  2643. """
  2644. if True:
  2645. tmp_arr = []
  2646. for index in range(len(assistant_anchor_type_var)):
  2647. if assistant_anchor_type_var[str(index)].value:
  2648. tmp_arr.append(common.find_keys_by_value(assistant_anchor_type_mapping, assistant_anchor_type_var[str(index)].text)[0])
  2649. # logger.info(tmp_arr)
  2650. config_data["assistant_anchor"]["type"] = tmp_arr
  2651. config_mapping = {
  2652. "assistant_anchor": {
  2653. "enable": (switch_assistant_anchor_enable, 'bool'),
  2654. "username": (input_assistant_anchor_username, 'str'),
  2655. "audio_synthesis_type": (select_assistant_anchor_audio_synthesis_type, 'str'),
  2656. "local_qa": {
  2657. "text": {
  2658. "enable": (switch_assistant_anchor_local_qa_text_enable, 'bool'),
  2659. "format": (select_assistant_anchor_local_qa_text_format, 'str'),
  2660. "file_path": (input_assistant_anchor_local_qa_text_file_path, 'str'),
  2661. "similarity": (input_assistant_anchor_local_qa_text_similarity, 'float')
  2662. },
  2663. "audio": {
  2664. "enable": (switch_assistant_anchor_local_qa_audio_enable, 'bool'),
  2665. "type": (select_assistant_anchor_local_qa_audio_type, 'str'),
  2666. "file_path": (input_assistant_anchor_local_qa_audio_file_path, 'str'),
  2667. "similarity": (input_assistant_anchor_local_qa_audio_similarity, 'float')
  2668. }
  2669. }
  2670. }
  2671. }
  2672. config_data = update_config(config_mapping, config, config_data, None)
  2673. """
  2674. 翻译
  2675. """
  2676. if True:
  2677. config_mapping = {
  2678. "translate": {
  2679. "enable": (switch_translate_enable, 'bool'),
  2680. "type": (select_translate_type, 'str'),
  2681. "trans_type": (select_translate_trans_type, 'str'),
  2682. "baidu": {
  2683. "appid": (input_translate_baidu_appid, 'str'),
  2684. "appkey": (input_translate_baidu_appkey, 'str'),
  2685. "from_lang": (select_translate_baidu_from_lang, 'str'),
  2686. "to_lang": (select_translate_baidu_to_lang, 'str'),
  2687. },
  2688. "google": {
  2689. "proxy": (input_translate_google_proxy, 'str'),
  2690. "src_lang": (select_translate_google_src_lang, 'str'),
  2691. "tgt_lang": (select_translate_google_tgt_lang, 'str'),
  2692. },
  2693. }
  2694. }
  2695. config_data = update_config(config_mapping, config, config_data, None)
  2696. """
  2697. 串口
  2698. """
  2699. if True:
  2700. tmp_arr = []
  2701. for index in range(len(serial_config_var) // 8):
  2702. tmp_json = {
  2703. "serial_name": "COM1",
  2704. "baudrate": "115200",
  2705. "serial_data_type": "ASCII"
  2706. }
  2707. tmp_json["serial_name"] = serial_config_var[str(8 * index)].value
  2708. tmp_json["baudrate"] = serial_config_var[str(8 * index + 1)].value
  2709. tmp_json["serial_data_type"] = serial_config_var[str(8 * index + 5)].value
  2710. tmp_arr.append(tmp_json)
  2711. # logger.info(tmp_arr)
  2712. config_data["serial"]["config"] = tmp_arr
  2713. """
  2714. 数据分析
  2715. """
  2716. if True:
  2717. config_mapping = {
  2718. "data_analysis": {
  2719. "comment_word_cloud": {
  2720. "top_num": (input_data_analysis_comment_word_cloud_top_num, 'int'),
  2721. },
  2722. "integral": {
  2723. "top_num": (input_data_analysis_integral_top_num, 'int'),
  2724. },
  2725. "gift": {
  2726. "top_num": (input_data_analysis_gift_top_num, 'int'),
  2727. },
  2728. }
  2729. }
  2730. config_data = update_config(config_mapping, config, config_data, None)
  2731. """
  2732. UI配置
  2733. """
  2734. if True:
  2735. config_mapping = {
  2736. "webui": {
  2737. "title": (input_webui_title, 'str'),
  2738. "ip": (input_webui_ip, 'str'),
  2739. "port": (input_webui_port, 'int'),
  2740. "auto_run": (switch_webui_auto_run, 'bool'),
  2741. "local_dir_to_endpoint": {
  2742. "enable": (switch_webui_local_dir_to_endpoint_enable, 'bool'),
  2743. },
  2744. "show_card": {
  2745. "common_config": {
  2746. "read_comment": (switch_webui_show_card_common_config_read_comment, 'bool'),
  2747. "filter": (switch_webui_show_card_common_config_filter, 'bool'),
  2748. "thanks": (switch_webui_show_card_common_config_thanks, 'bool'),
  2749. "local_qa": (switch_webui_show_card_common_config_local_qa, 'bool'),
  2750. "choose_song": (switch_webui_show_card_common_config_choose_song, 'bool'),
  2751. "sd": (switch_webui_show_card_common_config_sd, 'bool'),
  2752. "log": (switch_webui_show_card_common_config_log, 'bool'),
  2753. "schedule": (switch_webui_show_card_common_config_schedule, 'bool'),
  2754. "idle_time_task": (switch_webui_show_card_common_config_idle_time_task, 'bool'),
  2755. "trends_copywriting": (switch_webui_show_card_common_config_trends_copywriting, 'bool'),
  2756. "database": (switch_webui_show_card_common_config_database, 'bool'),
  2757. "play_audio": (switch_webui_show_card_common_config_play_audio, 'bool'),
  2758. "web_captions_printer": (switch_webui_show_card_common_config_web_captions_printer, 'bool'),
  2759. "key_mapping": (switch_webui_show_card_common_config_key_mapping, 'bool'),
  2760. "custom_cmd": (switch_webui_show_card_common_config_custom_cmd, 'bool'),
  2761. "trends_config": (switch_webui_show_card_common_config_trends_config, 'bool'),
  2762. "abnormal_alarm": (switch_webui_show_card_common_config_abnormal_alarm, 'bool'),
  2763. "coordination_program": (switch_webui_show_card_common_config_coordination_program, 'bool'),
  2764. },
  2765. "llm": {
  2766. "chatgpt": (switch_webui_show_card_llm_chatgpt, 'bool'),
  2767. "claude": (switch_webui_show_card_llm_claude, 'bool'),
  2768. "chatglm": (switch_webui_show_card_llm_chatglm, 'bool'),
  2769. "qwen": (switch_webui_show_card_llm_qwen, 'bool'),
  2770. "zhipu": (switch_webui_show_card_llm_zhipu, 'bool'),
  2771. "chat_with_file": (switch_webui_show_card_llm_chat_with_file, 'bool'),
  2772. "langchain_chatglm": (switch_webui_show_card_llm_langchain_chatglm, 'bool'),
  2773. "langchain_chatchat": (switch_webui_show_card_llm_langchain_chatchat, 'bool'),
  2774. "chatterbot": (switch_webui_show_card_llm_chatterbot, 'bool'),
  2775. "text_generation_webui": (switch_webui_show_card_llm_text_generation_webui, 'bool'),
  2776. "sparkdesk": (switch_webui_show_card_llm_sparkdesk, 'bool'),
  2777. "bard": (switch_webui_show_card_llm_bard, 'bool'),
  2778. "tongyi": (switch_webui_show_card_llm_tongyi, 'bool'),
  2779. "tongyixingchen": (switch_webui_show_card_llm_tongyixingchen, 'bool'),
  2780. "my_wenxinworkshop": (switch_webui_show_card_llm_my_wenxinworkshop, 'bool'),
  2781. "gemini": (switch_webui_show_card_llm_gemini, 'bool'),
  2782. "qanything": (switch_webui_show_card_llm_qanything, 'bool'),
  2783. "koboldcpp": (switch_webui_show_card_llm_koboldcpp, 'bool'),
  2784. "anythingllm": (switch_webui_show_card_llm_anythingllm, 'bool'),
  2785. "gpt4free": (switch_webui_show_card_llm_gpt4free, 'bool'),
  2786. "custom_llm": (switch_webui_show_card_llm_custom_llm, 'bool'),
  2787. "llm_tpu": (switch_webui_show_card_llm_llm_tpu, 'bool'),
  2788. "dify": (switch_webui_show_card_llm_dify, 'bool'),
  2789. },
  2790. "tts": {
  2791. "edge-tts": (switch_webui_show_card_tts_edge_tts, 'bool'),
  2792. "vits": (switch_webui_show_card_tts_vits, 'bool'),
  2793. "bert_vits2": (switch_webui_show_card_tts_bert_vits2, 'bool'),
  2794. "vits_fast": (switch_webui_show_card_tts_vits_fast, 'bool'),
  2795. "elevenlabs": (switch_webui_show_card_tts_elevenlabs, 'bool'),
  2796. "bark_gui": (switch_webui_show_card_tts_bark_gui, 'bool'),
  2797. "vall_e_x": (switch_webui_show_card_tts_vall_e_x, 'bool'),
  2798. "openai_tts": (switch_webui_show_card_tts_openai_tts, 'bool'),
  2799. "reecho_ai": (switch_webui_show_card_tts_reecho_ai, 'bool'),
  2800. "gradio_tts": (switch_webui_show_card_tts_gradio_tts, 'bool'),
  2801. "gpt_sovits": (switch_webui_show_card_tts_gpt_sovits, 'bool'),
  2802. "clone_voice": (switch_webui_show_card_tts_clone_voice, 'bool'),
  2803. "azure_tts": (switch_webui_show_card_tts_azure_tts, 'bool'),
  2804. "fish_speech": (switch_webui_show_card_tts_fish_speech, 'bool'),
  2805. "chattts": (switch_webui_show_card_tts_chattts, 'bool'),
  2806. "cosyvoice": (switch_webui_show_card_tts_cosyvoice, 'bool'),
  2807. },
  2808. "svc": {
  2809. "ddsp_svc": (switch_webui_show_card_svc_ddsp_svc, 'bool'),
  2810. "so_vits_svc": (switch_webui_show_card_svc_so_vits_svc, 'bool'),
  2811. },
  2812. "visual_body": {
  2813. "live2d": (switch_webui_show_card_visual_body_live2d, 'bool'),
  2814. "xuniren": (switch_webui_show_card_visual_body_xuniren, 'bool'),
  2815. "metahuman_stream": (switch_webui_show_card_visual_body_metahuman_stream, 'bool'),
  2816. "unity": (switch_webui_show_card_visual_body_unity, 'bool'),
  2817. "EasyAIVtuber": (switch_webui_show_card_visual_body_EasyAIVtuber, 'bool'),
  2818. "digital_human_video_player": (switch_webui_show_card_visual_body_digital_human_video_player, 'bool'),
  2819. "live2d_TTS_LLM_GPT_SoVITS_Vtuber": (switch_webui_show_card_visual_body_live2d_TTS_LLM_GPT_SoVITS_Vtuber, 'bool'),
  2820. },
  2821. },
  2822. "theme": {
  2823. "choose": (select_webui_theme_choose, 'str'),
  2824. },
  2825. },
  2826. "login": {
  2827. "enable": (switch_login_enable, 'bool'),
  2828. "username": (input_login_username, 'str'),
  2829. "password": (input_login_password, 'str'),
  2830. }
  2831. }
  2832. config_data = update_config(config_mapping, config, config_data, None)
  2833. tmp_arr = []
  2834. for index in range(len(webui_local_dir_to_endpoint_config_var) // 2):
  2835. tmp_json = {
  2836. "url_path": "",
  2837. "local_dir": ""
  2838. }
  2839. tmp_json["url_path"] = webui_local_dir_to_endpoint_config_var[str(2 * index)].value
  2840. tmp_json["local_dir"] = webui_local_dir_to_endpoint_config_var[str(2 * index + 1)].value
  2841. tmp_arr.append(tmp_json)
  2842. # logger.info(tmp_arr)
  2843. config_data["webui"]["local_dir_to_endpoint"]["config"] = tmp_arr
  2844. return config_data
  2845. except Exception as e:
  2846. logger.error(f"无法读取webui配置到变量!\n{e}")
  2847. ui.notify(position="top", type="negative", message=f"无法读取webui配置到变量!\n{e}")
  2848. logger.error(traceback.format_exc())
  2849. return None
  2850. # 保存配置
  2851. def save_config():
  2852. """
  2853. 保存配置到本地配置文件中
  2854. """
  2855. global config, config_path
  2856. # 配置检查
  2857. if not check_config():
  2858. return False
  2859. try:
  2860. with open(config_path, 'r', encoding="utf-8") as config_file:
  2861. config_data = json.load(config_file)
  2862. except Exception as e:
  2863. logger.error(f"无法读取配置文件!\n{e}")
  2864. ui.notify(position="top", type="negative", message=f"无法读取配置文件!{e}")
  2865. return False
  2866. # 读取webui配置到dict变量
  2867. config_data = webui_config_to_dict(config_data)
  2868. if config_data is None:
  2869. return False
  2870. # 写入本地问答json数据到文件
  2871. try:
  2872. ret = common.write_content_to_file(input_local_qa_text_json_file_path.value, textarea_local_qa_text_json_file_content.value, write_log=False)
  2873. if not ret:
  2874. ui.notify(position="top", type="negative", message="无法写入本地问答json数据到文件!\n详细报错见日志")
  2875. return False
  2876. except Exception as e:
  2877. logger.error(f"无法写入本地问答json数据到文件!\n{str(e)}")
  2878. ui.notify(position="top", type="negative", message=f"无法写入本地问答json数据到文件!\n{str(e)}")
  2879. return False
  2880. # 写入配置到配置文件
  2881. try:
  2882. with open(config_path, 'w', encoding="utf-8") as config_file:
  2883. json.dump(config_data, config_file, indent=2, ensure_ascii=False)
  2884. config_file.flush() # 刷新缓冲区,确保写入立即生效
  2885. logger.info("配置数据已成功写入文件!")
  2886. ui.notify(position="top", type="positive", message="配置数据已成功写入文件!")
  2887. return True
  2888. except Exception as e:
  2889. logger.error(f"无法写入配置文件!\n{str(e)}")
  2890. ui.notify(position="top", type="negative", message=f"无法写入配置文件!\n{str(e)}")
  2891. return False
  2892. """
  2893. ..............................................................................................................
  2894. ..............................................................................................................
  2895. ..........................,]].................................................................................
  2896. .........................O@@@@^...............................................................................
  2897. .....=@@@@@`.....O@@@....,\@@[.....................................,@@@@@@@@@@]....O@@@^......=@@@@....O@@@^..
  2898. .....=@@@@@@.....O@@@............................................=@@@@/`..,[@@/....O@@@^......=@@@@....O@@@^..
  2899. .....=@@@@@@@....O@@@....,]]]].......]@@@@@]`.....,/@@@@\`....../@@@@..............O@@@^......=@@@@....O@@@^..
  2900. .....=@@@/@@@\...O@@@....=@@@@....,@@@@@@@@@@^..,@@@@@@@@@@\...=@@@@...............O@@@^......=@@@@....O@@@^..
  2901. .....=@@@^,@@@\..O@@@....=@@@@...,@@@@`........=@@@/....=@@@\..=@@@@....]]]]]]]]...O@@@^......=@@@@....O@@@^..
  2902. .....=@@@^.=@@@^.O@@@....=@@@@...O@@@^.........@@@@......@@@@..=@@@@....=@@@@@@@...O@@@^......=@@@@....O@@@^..
  2903. .....=@@@^..\@@@^=@@@....=@@@@...@@@@^........,@@@@@@@@@@@@@@..=@@@@.......=@@@@...O@@@^......=@@@@....O@@@^..
  2904. .....=@@@^...\@@@/@@@....=@@@@...O@@@^.........@@@@`...........,@@@@`......=@@@@...O@@@^......=@@@@....O@@@^..
  2905. .....=@@@^....@@@@@@@....=@@@@...,@@@@`........=@@@@......,.....=@@@@`.....=@@@@...=@@@@`.....@@@@^....O@@@^..
  2906. .....=@@@^....,@@@@@@....=@@@@....,@@@@@@@@@@`..=@@@@@@@@@@@`....,@@@@@@@@@@@@@@....,@@@@@@@@@@@@`.....O@@@^..
  2907. .....,[[[`.....,[[[[[....,[[[[.......[@@@@@[`.....,[@@@@@[`.........,\@@@@@@[`.........[@@@@@@[........[[[[`..
  2908. ..............................................................................................................
  2909. ..............................................................................................................
  2910. """
  2911. # 语音合成所有配置项
  2912. audio_synthesis_type_options = {
  2913. 'edge-tts': 'Edge-TTS',
  2914. 'vits': 'VITS',
  2915. 'bert_vits2': 'bert_vits2',
  2916. 'vits_fast': 'VITS-Fast',
  2917. 'elevenlabs': 'elevenlabs',
  2918. #'genshinvoice_top': 'genshinvoice_top',
  2919. #'tts_ai_lab_top': 'tts_ai_lab_top',
  2920. 'bark_gui': 'bark_gui',
  2921. 'vall_e_x': 'VALL-E-X',
  2922. 'openai_tts': 'OpenAI TTS',
  2923. 'reecho_ai': '睿声AI',
  2924. 'gradio_tts': 'Gradio',
  2925. 'gpt_sovits': 'GPT_SoVITS',
  2926. 'clone_voice': 'clone-voice',
  2927. 'azure_tts': 'azure_tts',
  2928. 'fish_speech': 'fish_speech',
  2929. 'chattts': 'ChatTTS',
  2930. 'cosyvoice': 'CosyVoice',
  2931. }
  2932. # 聊天类型所有配置项
  2933. chat_type_options = {
  2934. 'none': '不启用',
  2935. 'reread': '复读机',
  2936. 'chatgpt': 'ChatGPT/闻达',
  2937. 'claude': 'Claude',
  2938. 'claude2': 'Claude2',
  2939. 'chatglm': 'ChatGLM',
  2940. 'qwen': 'Qwen',
  2941. 'chat_with_file': 'chat_with_file',
  2942. 'chatterbot': 'Chatterbot',
  2943. 'text_generation_webui': 'text_generation_webui',
  2944. 'sparkdesk': '讯飞星火',
  2945. 'langchain_chatglm': 'langchain_chatglm',
  2946. 'langchain_chatchat': 'langchain_chatchat',
  2947. 'zhipu': '智谱AI',
  2948. 'bard': 'Bard',
  2949. 'tongyixingchen': '通义星尘',
  2950. 'my_wenxinworkshop': '千帆大模型',
  2951. 'gemini': 'Gemini',
  2952. 'qanything': 'QAnything',
  2953. 'koboldcpp': 'koboldcpp',
  2954. 'anythingllm': 'AnythingLLM',
  2955. 'tongyi': '通义千问/阿里云百炼',
  2956. 'gpt4free': 'GPT4Free',
  2957. 'dify': 'Dify',
  2958. 'volcengine': '火山引擎',
  2959. 'llm_tpu': 'LLM_TPU',
  2960. 'custom_llm': '自定义LLM',
  2961. }
  2962. platform_options = {
  2963. 'talk': '聊天模式',
  2964. 'bilibili': '哔哩哔哩',
  2965. 'bilibili2': '哔哩哔哩2',
  2966. 'dy': '抖音',
  2967. 'dy2': '抖音2',
  2968. 'ks': '快手',
  2969. 'ks2': '快手2',
  2970. 'pdd': '拼多多',
  2971. 'wxlive': '微信视频号',
  2972. '1688': '1688',
  2973. 'douyu': '斗鱼',
  2974. 'youtube': 'YouTube',
  2975. 'twitch': 'twitch',
  2976. 'tiktok': 'tiktok',
  2977. }
  2978. visual_body_options = {
  2979. '其他': '其他(外置)',
  2980. 'metahuman_stream': 'metahuman_stream',
  2981. 'EasyAIVtuber': 'EasyAIVtuber',
  2982. 'digital_human_video_player': '数字人视频播放器',
  2983. 'live2d_TTS_LLM_GPT_SoVITS_Vtuber': 'live2d-TTS-LLM-GPT-SoVITS-Vtuber',
  2984. 'xuniren': 'xuniren',
  2985. }
  2986. with ui.tabs().classes('w-full') as tabs:
  2987. common_config_page = ui.tab('通用配置')
  2988. llm_page = ui.tab('大语言模型')
  2989. tts_page = ui.tab('文本转语音')
  2990. svc_page = ui.tab('变声')
  2991. visual_body_page = ui.tab('虚拟身体')
  2992. copywriting_page = ui.tab('文案')
  2993. talk_page = ui.tab('聊天')
  2994. image_recognition_page = ui.tab('图像识别')
  2995. integral_page = ui.tab('积分')
  2996. assistant_anchor_page = ui.tab('助播')
  2997. translate_page = ui.tab('翻译')
  2998. serial_page = ui.tab('串口')
  2999. data_analysis_page = ui.tab('数据分析')
  3000. web_page = ui.tab('页面配置')
  3001. docs_page = ui.tab('文档&教程')
  3002. about_page = ui.tab('关于')
  3003. with ui.tab_panels(tabs, value=common_config_page).classes('w-full'):
  3004. with ui.tab_panel(common_config_page).style(tab_panel_css):
  3005. with ui.row():
  3006. select_platform = ui.select(
  3007. label='平台',
  3008. options=platform_options,
  3009. value=config.get("platform")
  3010. ).style("width:200px;")
  3011. input_room_display_id = ui.input(label='直播间号', placeholder='一般为直播间URL最后/后面的字母或数字', value=config.get("room_display_id")).style("width:200px;").tooltip('一般为直播间URL最后/后面的字母或数字')
  3012. select_chat_type = ui.select(
  3013. label='大语言模型',
  3014. options=chat_type_options,
  3015. value=config.get("chat_type")
  3016. ).style("width:200px;").tooltip('选用的LLM类型。相关的弹幕信息等会传递给此LLM进行推理,获取回答')
  3017. select_visual_body = ui.select(
  3018. label='虚拟身体',
  3019. options=visual_body_options,
  3020. value=config.get("visual_body")
  3021. ).style("width:200px;").tooltip('选用的虚拟身体类型。如果使用VTS对接,就选其他,用什么展示身体就选什么,大部分对接的选项需要单独启动对应的服务端程序,请勿随便选择。')
  3022. select_audio_synthesis_type = ui.select(
  3023. label='语音合成',
  3024. options=audio_synthesis_type_options,
  3025. value=config.get("audio_synthesis_type")
  3026. ).style("width:200px;").tooltip('选用的TTS类型,所有的文本内容最终都将通过此TTS进行语音合成')
  3027. with ui.row():
  3028. select_need_lang = ui.select(
  3029. label='回复语言',
  3030. options={'none': '所有', 'zh': '中文', 'en': '英文', 'jp': '日文'},
  3031. value=config.get("need_lang")
  3032. ).style("width:200px;").tooltip('限制回复的语言,如:选中中文,则只会回复中文提问,其他语言将被跳过')
  3033. input_before_prompt = ui.input(label='提示词前缀', placeholder='此配置会追加在弹幕前,再发送给LLM处理', value=config.get("before_prompt")).style("width:200px;").tooltip('此配置会追加在弹幕前,再发送给LLM处理')
  3034. input_after_prompt = ui.input(label='提示词后缀', placeholder='此配置会追加在弹幕后,再发送给LLM处理', value=config.get("after_prompt")).style("width:200px;").tooltip('此配置会追加在弹幕后,再发送给LLM处理')
  3035. switch_comment_template_enable = ui.switch('启用弹幕模板', value=config.get("comment_template", "enable")).style(switch_internal_css).tooltip('此配置会追加在弹幕后,再发送给LLM处理')
  3036. input_comment_template_copywriting = ui.input(label='弹幕模板', value=config.get("comment_template", "copywriting"), placeholder='此配置会对弹幕内容进行修改,{}内为变量,会被替换为指定内容,请勿随意删除变量').style("width:200px;").tooltip('此配置会对弹幕内容进行修改,{}内为变量,会被替换为指定内容,请勿随意删除变量')
  3037. switch_reply_template_enable = ui.switch('启用回复模板', value=config.get("reply_template", "enable")).style(switch_internal_css).tooltip('此配置会在LLM输出的答案中进行回复内容的重新构建')
  3038. input_reply_template_username_max_len = ui.input(label='回复用户名的最大长度', value=config.get("reply_template", "username_max_len"), placeholder='回复用户名的最大长度').style("width:200px;").tooltip('回复用户名的最大长度')
  3039. textarea_reply_template_copywriting = ui.textarea(
  3040. label='回复模板',
  3041. placeholder='此配置会对LLM回复内容进行修改,{}内为变量,会被替换为指定内容,请勿随意删除变量',
  3042. value=textarea_data_change(config.get("reply_template", "copywriting"))
  3043. ).style("width:500px;").tooltip('此配置会对LLM回复内容进行修改,{}内为变量,会被替换为指定内容,请勿随意删除变量')
  3044. with ui.expansion('功能管理', icon="settings", value=True).classes('w-full'):
  3045. with ui.card().style(card_css):
  3046. ui.label('平台相关')
  3047. with ui.card().style(card_css):
  3048. ui.label('哔哩哔哩')
  3049. with ui.row():
  3050. select_bilibili_login_type = ui.select(
  3051. label='登录方式',
  3052. options={'手机扫码': '手机扫码', '手机扫码-终端': '手机扫码-终端', 'cookie': 'cookie', '账号密码登录': '账号密码登录', 'open_live': '开放平台', '不登录': '不登录'},
  3053. value=config.get("bilibili", "login_type")
  3054. ).style("width:100px")
  3055. input_bilibili_cookie = ui.input(label='cookie', placeholder='b站登录后F12抓网络包获取cookie,强烈建议使用小号!有封号风险,虽然实际上没听说有人被封过', value=config.get("bilibili", "cookie")).style("width:500px;").tooltip('b站登录后F12抓网络包获取cookie,强烈建议使用小号!有封号风险,虽然实际上没听说有人被封过')
  3056. input_bilibili_ac_time_value = ui.input(label='ac_time_value', placeholder='b站登录后,F12控制台,输入window.localStorage.ac_time_value获取(如果没有,请重新登录)', value=config.get("bilibili", "ac_time_value")).style("width:500px;").tooltip('仅在平台:哔哩哔哩,情况下可选填写。b站登录后,F12控制台,输入window.localStorage.ac_time_value获取(如果没有,请重新登录)')
  3057. with ui.row():
  3058. input_bilibili_username = ui.input(label='账号', value=config.get("bilibili", "username"), placeholder='b站账号(建议使用小号)').style("width:300px;").tooltip('仅在平台:哔哩哔哩,登录方式:账号密码登录,情况下填写。b站账号(建议使用小号)')
  3059. input_bilibili_password = ui.input(label='密码', value=config.get("bilibili", "password"), placeholder='b站密码(建议使用小号)').style("width:300px;").tooltip('仅在平台:哔哩哔哩,登录方式:账号密码登录,情况下填写。b站密码(建议使用小号)')
  3060. with ui.row():
  3061. with ui.card().style(card_css):
  3062. ui.label('开放平台')
  3063. with ui.row():
  3064. input_bilibili_open_live_ACCESS_KEY_ID = ui.input(label='ACCESS_KEY_ID', value=config.get("bilibili", "open_live", "ACCESS_KEY_ID"), placeholder='开放平台ACCESS_KEY_ID').style("width:160px;").tooltip('仅在平台:哔哩哔哩2,登录方式:开放平台,情况下填写。开放平台ACCESS_KEY_ID')
  3065. input_bilibili_open_live_ACCESS_KEY_SECRET = ui.input(label='ACCESS_KEY_SECRET', value=config.get("bilibili", "open_live", "ACCESS_KEY_SECRET"), placeholder='开放平台ACCESS_KEY_SECRET').style("width:200px;").tooltip('仅在平台:哔哩哔哩2,登录方式:开放平台,情况下填写。开放平台ACCESS_KEY_SECRET')
  3066. input_bilibili_open_live_APP_ID = ui.input(label='项目ID', value=config.get("bilibili", "open_live", "APP_ID"), placeholder='开放平台 创作者服务中心 项目ID').style("width:100px;").tooltip('仅在平台:哔哩哔哩2,登录方式:开放平台,情况下填写。开放平台 创作者服务中心 项目ID')
  3067. input_bilibili_open_live_ROOM_OWNER_AUTH_CODE = ui.input(label='身份码', value=config.get("bilibili", "open_live", "ROOM_OWNER_AUTH_CODE"), placeholder='直播中心用户 身份码').style("width:100px;").tooltip('仅在平台:哔哩哔哩2,登录方式:开放平台,情况下填写。直播中心用户 身份码')
  3068. with ui.card().style(card_css):
  3069. ui.label('twitch')
  3070. with ui.row():
  3071. input_twitch_token = ui.input(label='token', value=config.get("twitch", "token"), placeholder='访问 https://twitchapps.com/tmi/ 获取,格式为:oauth:xxx').style("width:300px;")
  3072. input_twitch_user = ui.input(label='用户名', value=config.get("twitch", "user"), placeholder='你的twitch账号用户名').style("width:300px;")
  3073. input_twitch_proxy_server = ui.input(label='HTTP代理IP地址', value=config.get("twitch", "proxy_server"), placeholder='代理软件,http协议监听的ip地址,一般为:127.0.0.1').style("width:200px;")
  3074. input_twitch_proxy_port = ui.input(label='HTTP代理端口', value=config.get("twitch", "proxy_port"), placeholder='代理软件,http协议监听的端口,一般为:1080').style("width:200px;")
  3075. if config.get("webui", "show_card", "common_config", "play_audio"):
  3076. with ui.card().style(card_css):
  3077. ui.label('音频播放')
  3078. with ui.row():
  3079. switch_play_audio_enable = ui.switch('启用', value=config.get("play_audio", "enable")).style(switch_internal_css)
  3080. switch_play_audio_text_split_enable = ui.switch('启用文本切分', value=config.get("play_audio", "text_split_enable")).style(switch_internal_css).tooltip('启用后会将LLM等待合成音频的消息根据内部切分算法切分成多个短句,以便TTS快速合成')
  3081. switch_play_audio_info_to_callback = ui.switch('音频信息回传给内部接口', value=config.get("play_audio", "info_to_callback")).style(switch_internal_css).tooltip('启用后,会在当前音频播放完毕后,将程序中等待播放的音频信息传递给内部接口,用于闲时任务的闲时清零功能。\n不过这个功能会一定程度的拖慢程序运行,如果你不需要闲时清零,可以关闭此功能来提高响应速度')
  3082. with ui.row():
  3083. input_play_audio_interval_num_min = ui.input(label='间隔时间重复次数最小值', value=config.get("play_audio", "interval_num_min"), placeholder='普通音频播放间隔时间,重复睡眠次数最小值。会在最大最小值之间随机生成一个重复次数,就是 次数 x 时间 = 最终间隔时间').tooltip('普通音频播放间隔时间重复睡眠次数最小值。会在最大最小值之间随机生成一个重复次数,就是 次数 x 时间 = 最终间隔时间')
  3084. input_play_audio_interval_num_max = ui.input(label='间隔时间重复次数最大值', value=config.get("play_audio", "interval_num_max"), placeholder='普通音频播放间隔时间,重复睡眠次数最大值。会在最大最小值之间随机生成一个重复次数,就是 次数 x 时间 = 最终间隔时间').tooltip('普通音频播放间隔时间重复睡眠次数最大值。会在最大最小值之间随机生成一个重复次数,就是 次数 x 时间 = 最终间隔时间')
  3085. input_play_audio_normal_interval_min = ui.input(label='普通音频播放间隔最小值', value=config.get("play_audio", "normal_interval_min"), placeholder='就是弹幕回复、唱歌等音频播放结束后到播放下一个音频之间的一个间隔时间,单位:秒').tooltip('就是弹幕回复、唱歌等音频播放结束后到播放下一个音频之间的一个间隔时间,单位:秒。次数 x 时间 = 最终间隔时间')
  3086. input_play_audio_normal_interval_max = ui.input(label='普通音频播放间隔最大值', value=config.get("play_audio", "normal_interval_max"), placeholder='就是弹幕回复、唱歌等音频播放结束后到播放下一个音频之间的一个间隔时间,单位:秒').tooltip('就是弹幕回复、唱歌等音频播放结束后到播放下一个音频之间的一个间隔时间,单位:秒。次数 x 时间 = 最终间隔时间')
  3087. input_play_audio_out_path = ui.input(label='音频输出路径', placeholder='音频文件合成后存储的路径,支持相对路径或绝对路径', value=config.get("play_audio", "out_path")).tooltip('音频文件合成后存储的路径,支持相对路径或绝对路径')
  3088. select_play_audio_player = ui.select(
  3089. label='音频播放器',
  3090. options={'pygame': 'pygame', 'audio_player_v2': 'audio_player_v2', 'audio_player': 'audio_player'},
  3091. value=config.get("play_audio", "player")
  3092. ).style("width:200px").tooltip('选用的音频播放器,默认pygame不需要再安装其他程序。audio player需要单独安装对接,详情看视频教程')
  3093. with ui.card().style(card_css):
  3094. ui.label('audio_player')
  3095. with ui.row():
  3096. input_audio_player_api_ip_port = ui.input(
  3097. label='API地址',
  3098. value=config.get("audio_player", "api_ip_port"),
  3099. placeholder='audio_player的API地址,只需要 http://ip:端口 即可',
  3100. validation={
  3101. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3102. }
  3103. ).style("width:200px;").tooltip('仅在 音频播放器:audio_player等,情况下填写。audio_player的API地址,只需要 http://ip:端口 即可')
  3104. with ui.card().style(card_css):
  3105. ui.label('音频随机变速')
  3106. with ui.grid(columns=3):
  3107. switch_audio_random_speed_normal_enable = ui.switch('普通音频变速', value=config.get("audio_random_speed", "normal", "enable")).style(switch_internal_css).tooltip('是否启用 针对 普通音频的音频变速功能。此功能需要安装配置ffmpeg才能使用')
  3108. input_audio_random_speed_normal_speed_min = ui.input(label='速度下限', value=config.get("audio_random_speed", "normal", "speed_min")).style("width:200px;").tooltip('音频变速的下限,最终速度会在上下限之间随机一个值进行变速')
  3109. input_audio_random_speed_normal_speed_max = ui.input(label='速度上限', value=config.get("audio_random_speed", "normal", "speed_max")).style("width:200px;").tooltip('音频变速的上限,最终速度会在上下限之间随机一个值进行变速')
  3110. with ui.grid(columns=3):
  3111. switch_audio_random_speed_copywriting_enable = ui.switch('文案音频变速', value=config.get("audio_random_speed", "copywriting", "enable")).style(switch_internal_css).tooltip('是否启用 针对 文案页音频的音频变速功能。此功能需要安装配置ffmpeg才能使用')
  3112. input_audio_random_speed_copywriting_speed_min = ui.input(label='速度下限', value=config.get("audio_random_speed", "copywriting", "speed_min")).style("width:200px;").tooltip('音频变速的下限,最终速度会在上下限之间随机一个值进行变速')
  3113. input_audio_random_speed_copywriting_speed_max = ui.input(label='速度上限', value=config.get("audio_random_speed", "copywriting", "speed_max")).style("width:200px;").tooltip('音频变速的上限,最终速度会在上下限之间随机一个值进行变速')
  3114. if config.get("webui", "show_card", "common_config", "filter"):
  3115. with ui.card().style(card_css):
  3116. ui.label('过滤')
  3117. with ui.grid(columns=6):
  3118. textarea_filter_before_must_str = ui.textarea(label='弹幕触发前缀', placeholder='前缀必须携带其中任一字符串才能触发\n例如:配置#,那么这个会触发:#你好', value=textarea_data_change(config.get("filter", "before_must_str"))).style("width:200px;").tooltip("前缀必须携带其中任一字符串才能触发\n例如:配置#,那么这个会触发:#你好")
  3119. textarea_filter_after_must_str = ui.textarea(label='弹幕触发后缀', placeholder='后缀必须携带其中任一字符串才能触发\n例如:配置。那么这个会触发:你好。', value=textarea_data_change(config.get("filter", "before_must_str"))).style("width:200px;").tooltip("后缀必须携带其中任一字符串才能触发\n例如:配置。那么这个会触发:你好。")
  3120. textarea_filter_before_filter_str = ui.textarea(label='弹幕过滤前缀', placeholder='当前缀为其中任一字符串时,弹幕会被过滤\n例如:配置#,那么这个会被过滤:#你好', value=textarea_data_change(config.get("filter", "before_filter_str"))).style("width:200px;").tooltip("当前缀为其中任一字符串时,弹幕会被过滤\n例如:配置#,那么这个会被过滤:#你好")
  3121. textarea_filter_after_filter_str = ui.textarea(label='弹幕过滤后缀', placeholder='当后缀为其中任一字符串时,弹幕会被过滤\n例如:配置#,那么这个会被过滤:你好#', value=textarea_data_change(config.get("filter", "before_filter_str"))).style("width:200px;").tooltip("当后缀为其中任一字符串时,弹幕会被过滤\n例如:配置#,那么这个会被过滤:你好#")
  3122. textarea_filter_before_must_str_for_llm = ui.textarea(label='LLM触发前缀', placeholder='前缀必须携带其中任一字符串才能触发LLM\n例如:配置#,那么这个会触发:#你好', value=textarea_data_change(config.get("filter", "before_must_str_for_llm"))).style("width:200px;").tooltip("前缀必须携带其中任一字符串才能触发LLM\n例如:配置#,那么这个会触发:#你好")
  3123. textarea_filter_after_must_str_for_llm = ui.textarea(label='LLM触发后缀', placeholder='后缀必须携带其中任一字符串才能触发LLM\n例如:配置。那么这个会触发:你好。', value=textarea_data_change(config.get("filter", "before_must_str_for_llm"))).style("width:200px;").tooltip('后缀必须携带其中任一字符串才能触发LLM\n例如:配置。那么这个会触发:你好。')
  3124. with ui.row():
  3125. input_filter_max_len = ui.input(label='最大单词数', placeholder='最长阅读的英文单词数(空格分隔)', value=config.get("filter", "max_len")).style("width:150px;").tooltip('最长阅读的英文单词数(空格分隔)')
  3126. input_filter_max_char_len = ui.input(label='最大字符数', placeholder='最长阅读的字符数,双重过滤,避免溢出', value=config.get("filter", "max_char_len")).style("width:150px;").tooltip('最长阅读的字符数,双重过滤,避免溢出')
  3127. switch_filter_username_convert_digits_to_chinese = ui.switch('用户名中的数字转中文', value=config.get("filter", "username_convert_digits_to_chinese")).style(switch_internal_css).tooltip('用户名中的数字转中文')
  3128. switch_filter_emoji = ui.switch('弹幕表情过滤', value=config.get("filter", "emoji")).style(switch_internal_css)
  3129. with ui.grid(columns=5):
  3130. switch_filter_badwords_enable = ui.switch('违禁词过滤', value=config.get("filter", "badwords", "enable")).style(switch_internal_css)
  3131. switch_filter_badwords_discard = ui.switch('违禁语句丢弃', value=config.get("filter", "badwords", "discard")).style(switch_internal_css)
  3132. input_filter_badwords_path = ui.input(label='违禁词路径', value=config.get("filter", "badwords", "path"), placeholder='本地违禁词数据路径(你如果不需要,可以清空文件内容)').style("width:200px;").tooltip('本地违禁词数据路径(你如果不需要,可以清空文件内容)')
  3133. input_filter_badwords_bad_pinyin_path = ui.input(label='违禁拼音路径', value=config.get("filter", "badwords", "bad_pinyin_path"), placeholder='本地违禁拼音数据路径(你如果不需要,可以清空文件内容)').style("width:200px;").tooltip('本地违禁拼音数据路径(你如果不需要,可以清空文件内容)')
  3134. input_filter_badwords_replace = ui.input(label='违禁词替换', value=config.get("filter", "badwords", "replace"), placeholder='在不丢弃违禁语句的前提下,将违禁词替换成此项的文本').style("width:200px;").tooltip('在不丢弃违禁语句的前提下,将违禁词替换成此项的文本')
  3135. with ui.expansion('消息遗忘&保留设置', icon="settings", value=True).classes('w-full'):
  3136. with ui.element('div').classes('p-2 bg-blue-100'):
  3137. ui.label("遗忘间隔 指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,但会保留最新的n个数据;保留数 指的是保留最新收到的数据的数量")
  3138. with ui.grid(columns=4):
  3139. input_filter_comment_forget_duration = ui.input(
  3140. label='弹幕遗忘间隔',
  3141. placeholder='例:1',
  3142. value=config.get("filter", "comment_forget_duration")
  3143. ).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3144. input_filter_comment_forget_reserve_num = ui.input(label='弹幕保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "comment_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3145. input_filter_gift_forget_duration = ui.input(label='礼物遗忘间隔', placeholder='指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义', value=config.get("filter", "gift_forget_duration")).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3146. input_filter_gift_forget_reserve_num = ui.input(label='礼物保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "gift_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3147. with ui.grid(columns=4):
  3148. input_filter_entrance_forget_duration = ui.input(label='入场遗忘间隔', placeholder='指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义', value=config.get("filter", "entrance_forget_duration")).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3149. input_filter_entrance_forget_reserve_num = ui.input(label='入场保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "entrance_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3150. input_filter_follow_forget_duration = ui.input(label='关注遗忘间隔', placeholder='指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义', value=config.get("filter", "follow_forget_duration")).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3151. input_filter_follow_forget_reserve_num = ui.input(label='关注保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "follow_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3152. with ui.grid(columns=4):
  3153. input_filter_talk_forget_duration = ui.input(label='聊天遗忘间隔', placeholder='指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义', value=config.get("filter", "talk_forget_duration")).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3154. input_filter_talk_forget_reserve_num = ui.input(label='聊天保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "talk_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3155. input_filter_schedule_forget_duration = ui.input(label='定时遗忘间隔', placeholder='指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义', value=config.get("filter", "schedule_forget_duration")).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3156. input_filter_schedule_forget_reserve_num = ui.input(label='定时保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "schedule_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3157. with ui.grid(columns=4):
  3158. input_filter_idle_time_task_forget_duration = ui.input(label='闲时任务遗忘间隔', placeholder='指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义', value=config.get("filter", "idle_time_task_forget_duration")).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3159. input_filter_idle_time_task_forget_reserve_num = ui.input(label='闲时任务保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "idle_time_task_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3160. input_filter_image_recognition_schedule_forget_duration = ui.input(label='图像识别遗忘间隔', placeholder='指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义', value=config.get("filter", "image_recognition_schedule_forget_duration")).style("width:200px;").tooltip('指的是每隔这个间隔时间(秒),就会丢弃这个间隔时间中接收到的数据,\n保留数据在以下配置中可以自定义')
  3161. input_filter_image_recognition_schedule_forget_reserve_num = ui.input(label='图像识别保留数', placeholder='保留最新收到的数据的数量', value=config.get("filter", "image_recognition_schedule_forget_reserve_num")).style("width:200px;").tooltip('保留最新收到的数据的数量')
  3162. with ui.expansion('限定时间段内数据重复丢弃', icon="settings", value=True).classes('w-full'):
  3163. with ui.row():
  3164. switch_filter_limited_time_deduplication_enable = ui.switch('启用', value=config.get("filter", "limited_time_deduplication", "enable")).style(switch_internal_css)
  3165. input_filter_limited_time_deduplication_comment = ui.input(label='弹幕检测周期', value=config.get("filter", "limited_time_deduplication", "comment"), placeholder='在这个周期时间(秒)内,重复的数据将被丢弃').style("width:200px;").tooltip('在这个周期时间(秒)内,重复的数据将被丢弃')
  3166. input_filter_limited_time_deduplication_gift = ui.input(label='礼物检测周期', value=config.get("filter", "limited_time_deduplication", "gift"), placeholder='在这个周期时间(秒)内,重复的数据将被丢弃').style("width:200px;").tooltip('在这个周期时间(秒)内,重复的数据将被丢弃')
  3167. input_filter_limited_time_deduplication_entrance = ui.input(label='入场检测周期', value=config.get("filter", "limited_time_deduplication", "entrance"), placeholder='在这个周期时间(秒)内,重复的数据将被丢弃').style("width:200px;").tooltip('在这个周期时间(秒)内,重复的数据将被丢弃')
  3168. with ui.expansion('待合成音频的消息&待播放音频队列', icon="settings", value=True).classes('w-full'):
  3169. with ui.row():
  3170. input_filter_message_queue_max_len = ui.input(label='消息队列最大保留长度', placeholder='收到的消息,生成的文本内容,会根据优先级存入消息队列,当新消息的优先级低于队列中所有的消息且超过此长度时,此消息将被丢弃', value=config.get("filter", "message_queue_max_len")).style("width:160px;").tooltip('收到的消息,生成的文本内容,会根据优先级存入消息队列,当新消息的优先级低于队列中所有的消息且超过此长度时,此消息将被丢弃')
  3171. input_filter_voice_tmp_path_queue_max_len = ui.input(label='音频播放队列最大保留长度', placeholder='合成后的音频,会根据优先级存入待播放音频队列,当新音频的优先级低于队列中所有的音频且超过此长度时,此音频将被丢弃', value=config.get("filter", "voice_tmp_path_queue_max_len")).style("width:200px;").tooltip('合成后的音频,会根据优先级存入待播放音频队列,当新音频的优先级低于队列中所有的音频且超过此长度时,此音频将被丢弃')
  3172. input_filter_voice_tmp_path_queue_min_start_play = ui.input(
  3173. label='音频播放队列首次触发播放阈值',
  3174. placeholder='正整数 例如:20,如果你不想开播前缓冲一定数量的音频,请配置0',
  3175. value=config.get("filter", "voice_tmp_path_queue_min_start_play")
  3176. ).style("width:200px;").tooltip('此功能用于缓存一定数量的音频后再开始播放。如果你不想开播前缓冲一定数量的音频,请配置0;如果你想提前准备一些音频,如因为TTS合成慢的原因,可以配置此值,让TTS提前合成你的其他任务触发的内容')
  3177. with ui.element('div').classes('p-2 bg-blue-100'):
  3178. ui.label("下方优先级配置,请使用正整数。数字越大,优先级越高,就会优先合成音频播放")
  3179. ui.label("另外需要注意,由于shi山原因,目前这个队列内容是文本切分后计算的长度,所以如果回复内容过长,可能会有丢数据的情况")
  3180. with ui.grid(columns=4):
  3181. input_filter_priority_mapping_idle_time_task = ui.input(label='闲时任务 优先级', value=config.get("filter", "priority_mapping", "idle_time_task"), placeholder='数字越大,优先级越高,但这个并非文本,所以暂时没啥用,预留').style("width:200px;").tooltip('数字越大,优先级越高')
  3182. input_filter_priority_mapping_image_recognition_schedule = ui.input(label='图像识别 优先级', value=config.get("filter", "priority_mapping", "image_recognition_schedule"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3183. input_filter_priority_mapping_local_qa_audio = ui.input(label='本地问答-音频 优先级', value=config.get("filter", "priority_mapping", "local_qa_audio"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3184. input_filter_priority_mapping_comment = ui.input(label='弹幕回复 优先级', value=config.get("filter", "priority_mapping", "comment"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3185. with ui.grid(columns=5):
  3186. input_filter_priority_mapping_song = ui.input(label='点歌 优先级', value=config.get("filter", "priority_mapping", "song"), placeholder='数字越大,优先级越高,但这个并非文本,所以暂时没啥用,预留').style("width:200px;").tooltip('数字越大,优先级越高')
  3187. input_filter_priority_mapping_read_comment = ui.input(label='念弹幕 优先级', value=config.get("filter", "priority_mapping", "read_comment"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3188. input_filter_priority_mapping_entrance = ui.input(label='入场欢迎 优先级', value=config.get("filter", "priority_mapping", "entrance"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3189. input_filter_priority_mapping_gift = ui.input(label='礼物答谢 优先级', value=config.get("filter", "priority_mapping", "gift"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3190. input_filter_priority_mapping_follow = ui.input(label='关注答谢 优先级', value=config.get("filter", "priority_mapping", "follow"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3191. with ui.grid(columns=5):
  3192. input_filter_priority_mapping_talk = ui.input(label='聊天(语音输入) 优先级', value=config.get("filter", "priority_mapping", "talk"), placeholder='数字越大,优先级越高,但这个并非文本,所以暂时没啥用,预留').style("width:200px;").tooltip('数字越大,优先级越高')
  3193. input_filter_priority_mapping_reread = ui.input(label='复读 优先级', value=config.get("filter", "priority_mapping", "reread"), placeholder='数字越大,优先级越高,但这个并非文本,所以暂时没啥用,预留').style("width:200px;").tooltip('数字越大,优先级越高')
  3194. input_filter_priority_mapping_key_mapping = ui.input(label='按键映射 优先级', value=config.get("filter", "priority_mapping", "key_mapping"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3195. input_filter_priority_mapping_integral = ui.input(label='积分 优先级', value=config.get("filter", "priority_mapping", "integral"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3196. input_filter_priority_mapping_reread_top_priority = ui.input(label='最高优先级复读 优先级', value=config.get("filter", "priority_mapping", "reread_top_priority"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3197. with ui.grid(columns=4):
  3198. input_filter_priority_mapping_copywriting = ui.input(label='文案 优先级', value=config.get("filter", "priority_mapping", "copywriting"), placeholder='数字越大,优先级越高,文案页的文案,但这个并非文本,所以暂时没啥用,预留').style("width:200px;").tooltip('数字越大,优先级越高')
  3199. input_filter_priority_mapping_abnormal_alarm = ui.input(label='异常报警 优先级', value=config.get("filter", "priority_mapping", "abnormal_alarm"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3200. input_filter_priority_mapping_trends_copywriting = ui.input(label='动态文案 优先级', value=config.get("filter", "priority_mapping", "trends_copywriting"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3201. input_filter_priority_mapping_schedule = ui.input(label='定时任务 优先级', value=config.get("filter", "priority_mapping", "schedule"), placeholder='数字越大,优先级越高').style("width:200px;").tooltip('数字越大,优先级越高')
  3202. with ui.expansion('弹幕黑名单', icon="settings", value=True).classes('w-full'):
  3203. with ui.row():
  3204. switch_filter_blacklist_enable = ui.switch('启用', value=config.get("filter", "blacklist", "enable")).style(switch_internal_css)
  3205. with ui.row():
  3206. textarea_filter_blacklist_username = ui.textarea(label='用户名 黑名单', value=textarea_data_change(config.get("filter", "blacklist", "username")), placeholder='屏蔽此名单内所有用户的弹幕,用户名以换行分隔').style("width:500px;")
  3207. with ui.expansion('互动功能', icon="question_answer", value=True).classes('w-full'):
  3208. if config.get("webui", "show_card", "common_config", "read_comment"):
  3209. with ui.card().style(card_css):
  3210. ui.label('念弹幕')
  3211. with ui.grid(columns=4):
  3212. switch_read_comment_enable = ui.switch('启用', value=config.get("read_comment", "enable")).style(switch_internal_css)
  3213. switch_read_comment_read_username_enable = ui.switch('念用户名', value=config.get("read_comment", "read_username_enable")).style(switch_internal_css)
  3214. input_read_comment_username_max_len = ui.input(label='用户名最大长度', value=config.get("read_comment", "username_max_len"), placeholder='需要保留的用户名的最大长度,超出部分将被丢弃').style("width:100px;").tooltip('需要保留的用户名的最大长度,超出部分将被丢弃')
  3215. switch_read_comment_voice_change = ui.switch('变声', value=config.get("read_comment", "voice_change")).style(switch_internal_css)
  3216. with ui.grid(columns=2):
  3217. textarea_read_comment_read_username_copywriting = ui.textarea(
  3218. label='念用户名文案',
  3219. placeholder='念用户名时使用的文案,可以自定义编辑多个(换行分隔),实际中会随机一个使用',
  3220. value=textarea_data_change(config.get("read_comment", "read_username_copywriting"))
  3221. ).style("width:500px;").tooltip('念用户名时使用的文案,可以自定义编辑多个(换行分隔),实际中会随机一个使用')
  3222. with ui.row():
  3223. switch_read_comment_periodic_trigger_enable = ui.switch('周期性触发启用', value=config.get("read_comment", "periodic_trigger", "enable")).style(switch_internal_css)
  3224. input_read_comment_periodic_trigger_periodic_time_min = ui.input(
  3225. label='触发周期最小值',
  3226. value=config.get("read_comment", "periodic_trigger", "periodic_time_min"),
  3227. placeholder='例如:5'
  3228. ).style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3229. input_read_comment_periodic_trigger_periodic_time_max = ui.input(
  3230. label='触发周期最大值',
  3231. value=config.get("read_comment", "periodic_trigger", "periodic_time_max"),
  3232. placeholder='例如:10'
  3233. ).style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3234. input_read_comment_periodic_trigger_trigger_num_min = ui.input(
  3235. label='触发次数最小值',
  3236. value=config.get("read_comment", "periodic_trigger", "trigger_num_min"),
  3237. placeholder='例如:0'
  3238. ).style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3239. input_read_comment_periodic_trigger_trigger_num_max = ui.input(
  3240. label='触发次数最大值',
  3241. value=config.get("read_comment", "periodic_trigger", "trigger_num_max"),
  3242. placeholder='例如:1'
  3243. ).style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3244. if config.get("webui", "show_card", "common_config", "local_qa"):
  3245. with ui.card().style(card_css):
  3246. ui.label('本地问答')
  3247. with ui.row():
  3248. switch_local_qa_periodic_trigger_enable = ui.switch('周期性触发启用', value=config.get("local_qa", "periodic_trigger", "enable")).style(switch_internal_css)
  3249. input_local_qa_periodic_trigger_periodic_time_min = ui.input(label='触发周期最小值', value=config.get("local_qa", "periodic_trigger", "periodic_time_min"), placeholder='每隔这个周期的时间会触发n次此功能').style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3250. input_local_qa_periodic_trigger_periodic_time_max = ui.input(label='触发周期最大值', value=config.get("local_qa", "periodic_trigger", "periodic_time_max"), placeholder='每隔这个周期的时间会触发n次此功能').style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3251. input_local_qa_periodic_trigger_trigger_num_min = ui.input(label='触发次数最小值', value=config.get("local_qa", "periodic_trigger", "trigger_num_min"), placeholder='周期到后,会触发n次此功能').style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3252. input_local_qa_periodic_trigger_trigger_num_max = ui.input(label='触发次数最大值', value=config.get("local_qa", "periodic_trigger", "trigger_num_max"), placeholder='周期到后,会触发n次此功能').style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3253. with ui.grid(columns=5):
  3254. switch_local_qa_text_enable = ui.switch('启用文本匹配', value=config.get("local_qa", "text", "enable")).style(switch_internal_css)
  3255. select_local_qa_text_type = ui.select(
  3256. label='弹幕日志类型',
  3257. options={'json': '自定义json', 'text': '一问一答'},
  3258. value=config.get("local_qa", "text", "type")
  3259. )
  3260. input_local_qa_text_file_path = ui.input(label='文本问答数据路径', placeholder='本地问答文本数据存储路径', value=config.get("local_qa", "text", "file_path")).style("width:200px;")
  3261. input_local_qa_text_similarity = ui.input(label='文本最低相似度', placeholder='最低文本匹配相似度,就是说用户发送的内容和本地问答库中设定的内容的最低相似度。\n低了就会被当做一般弹幕处理', value=config.get("local_qa", "text", "similarity")).style("width:200px;")
  3262. input_local_qa_text_username_max_len = ui.input(label='用户名最大长度', value=config.get("local_qa", "text", "username_max_len"), placeholder='需要保留的用户名的最大长度,超出部分将被丢弃').style("width:100px;")
  3263. with ui.grid(columns=4):
  3264. switch_local_qa_audio_enable = ui.switch('启用音频匹配', value=config.get("local_qa", "audio", "enable")).style(switch_internal_css)
  3265. input_local_qa_audio_file_path = ui.input(label='音频存储路径', placeholder='本地问答音频文件存储路径', value=config.get("local_qa", "audio", "file_path")).style("width:200px;")
  3266. input_local_qa_audio_similarity = ui.input(label='音频最低相似度', placeholder='最低音频匹配相似度,就是说用户发送的内容和本地音频库中音频文件名的最低相似度。\n低了就会被当做一般弹幕处理', value=config.get("local_qa", "audio", "similarity")).style("width:200px;")
  3267. with ui.row():
  3268. input_local_qa_text_json_file_path = ui.input(label='json文件路径', placeholder='填写json文件路径,默认为本地问答文本数据存储路径', value=config.get("local_qa", "text", "file_path")).style("width:200px;").tooltip("填写json文件路径,默认为本地问答文本数据存储路径")
  3269. def local_qa_text_json_file_reload():
  3270. try:
  3271. # 只做了个判空 所以别乱填
  3272. if input_local_qa_text_json_file_path.value != "":
  3273. textarea_local_qa_text_json_file_content.value = json.dumps(common.read_file(input_local_qa_text_json_file_path.value, "dict"), ensure_ascii=False, indent=3)
  3274. except Exception as e:
  3275. logger.error(traceback.format_exc())
  3276. ui.notify(f"文件路径有误或其他问题。报错:{str(e)}", position="top", type="negative")
  3277. button_local_qa_text_json_file_reload = ui.button('加载文件', on_click=lambda: local_qa_text_json_file_reload(), color=button_internal_color).style(button_internal_css)
  3278. textarea_local_qa_text_json_file_content = ui.textarea(label='JSON文件内容', placeholder='注意格式!').style("width:700px;")
  3279. local_qa_text_json_file_reload()
  3280. if config.get("webui", "show_card", "common_config", "thanks"):
  3281. with ui.card().style(card_css):
  3282. ui.label('答谢')
  3283. with ui.row():
  3284. input_thanks_username_max_len = ui.input(label='用户名最大长度', value=config.get("thanks", "username_max_len"), placeholder='需要保留的用户名的最大长度,超出部分将被丢弃').style("width:100px;")
  3285. with ui.expansion('入场设置', icon="settings", value=True).classes('w-full'):
  3286. with ui.row():
  3287. switch_thanks_entrance_enable = ui.switch('启用入场欢迎', value=config.get("thanks", "entrance_enable")).style(switch_internal_css)
  3288. switch_thanks_entrance_random = ui.switch('随机选取', value=config.get("thanks", "entrance_random")).style(switch_internal_css)
  3289. textarea_thanks_entrance_copy = ui.textarea(label='入场文案', value=textarea_data_change(config.get("thanks", "entrance_copy")), placeholder='用户进入直播间的相关文案,请勿动 {username},此字符串用于替换用户名').style("width:500px;")
  3290. with ui.row():
  3291. switch_thanks_entrance_periodic_trigger_enable = ui.switch('周期性触发启用', value=config.get("thanks", "entrance", "periodic_trigger", "enable")).style(switch_internal_css)
  3292. input_thanks_entrance_periodic_trigger_periodic_time_min = ui.input(label='触发周期最小值', value=config.get("thanks", "entrance", "periodic_trigger", "periodic_time_min"), placeholder='每隔这个周期的时间会触发n次此功能').style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3293. input_thanks_entrance_periodic_trigger_periodic_time_max = ui.input(label='触发周期最大值', value=config.get("thanks", "entrance", "periodic_trigger", "periodic_time_max"), placeholder='每隔这个周期的时间会触发n次此功能').style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3294. input_thanks_entrance_periodic_trigger_trigger_num_min = ui.input(label='触发次数最小值', value=config.get("thanks", "entrance", "periodic_trigger", "trigger_num_min"), placeholder='周期到后,会触发n次此功能').style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3295. input_thanks_entrance_periodic_trigger_trigger_num_max = ui.input(label='触发次数最大值', value=config.get("thanks", "entrance", "periodic_trigger", "trigger_num_max"), placeholder='周期到后,会触发n次此功能').style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3296. with ui.expansion('礼物设置', icon="settings", value=True).classes('w-full'):
  3297. with ui.row():
  3298. switch_thanks_gift_enable = ui.switch('启用礼物答谢', value=config.get("thanks", "gift_enable")).style(switch_internal_css)
  3299. switch_thanks_gift_random = ui.switch('随机选取', value=config.get("thanks", "gift_random")).style(switch_internal_css)
  3300. textarea_thanks_gift_copy = ui.textarea(label='礼物文案', value=textarea_data_change(config.get("thanks", "gift_copy")), placeholder='用户赠送礼物的相关文案,请勿动 {username} 和 {gift_name},此字符串用于替换用户名和礼物名').style("width:500px;")
  3301. input_thanks_lowest_price = ui.input(label='最低答谢礼物价格', value=config.get("thanks", "lowest_price"), placeholder='设置最低答谢礼物的价格(元),低于这个设置的礼物不会触发答谢').style("width:100px;")
  3302. with ui.row():
  3303. switch_thanks_gift_periodic_trigger_enable = ui.switch('周期性触发启用', value=config.get("thanks", "gift", "periodic_trigger", "enable")).style(switch_internal_css)
  3304. input_thanks_gift_periodic_trigger_periodic_time_min = ui.input(label='触发周期最小值', value=config.get("thanks", "gift", "periodic_trigger", "periodic_time_min"), placeholder='每隔这个周期的时间会触发n次此功能').style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3305. input_thanks_gift_periodic_trigger_periodic_time_max = ui.input(label='触发周期最大值', value=config.get("thanks", "gift", "periodic_trigger", "periodic_time_max"), placeholder='每隔这个周期的时间会触发n次此功能').style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3306. input_thanks_gift_periodic_trigger_trigger_num_min = ui.input(label='触发次数最小值', value=config.get("thanks", "gift", "periodic_trigger", "trigger_num_min"), placeholder='周期到后,会触发n次此功能').style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3307. input_thanks_gift_periodic_trigger_trigger_num_max = ui.input(label='触发次数最大值', value=config.get("thanks", "gift", "periodic_trigger", "trigger_num_max"), placeholder='周期到后,会触发n次此功能').style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3308. with ui.expansion('关注设置', icon="settings", value=True).classes('w-full'):
  3309. with ui.row():
  3310. switch_thanks_follow_enable = ui.switch('启用关注答谢', value=config.get("thanks", "follow_enable")).style(switch_internal_css)
  3311. switch_thanks_follow_random = ui.switch('随机选取', value=config.get("thanks", "follow_random")).style(switch_internal_css)
  3312. textarea_thanks_follow_copy = ui.textarea(label='关注文案', value=textarea_data_change(config.get("thanks", "follow_copy")), placeholder='用户关注时的相关文案,请勿动 {username},此字符串用于替换用户名').style("width:500px;")
  3313. with ui.row():
  3314. switch_thanks_follow_periodic_trigger_enable = ui.switch(
  3315. '周期性触发启用',
  3316. value=config.get("thanks", "follow", "periodic_trigger", "enable")
  3317. ).style(switch_internal_css)
  3318. input_thanks_follow_periodic_trigger_periodic_time_min = ui.input(
  3319. label='触发周期最小值',
  3320. value=config.get("thanks", "follow", "periodic_trigger", "periodic_time_min"),
  3321. placeholder='每隔这个周期的时间会触发n次此功能'
  3322. ).style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3323. input_thanks_follow_periodic_trigger_periodic_time_max = ui.input(
  3324. label='触发周期最大值',
  3325. value=config.get("thanks", "follow", "periodic_trigger", "periodic_time_max"),
  3326. placeholder='每隔这个周期的时间会触发n次此功能'
  3327. ).style("width:100px;").tooltip('每隔这个周期的时间会触发n次此功能,周期时间从最大最小值之间随机生成')
  3328. input_thanks_follow_periodic_trigger_trigger_num_min = ui.input(
  3329. label='触发次数最小值',
  3330. value=config.get("thanks", "follow", "periodic_trigger", "trigger_num_min"),
  3331. placeholder='周期到后,会触发n次此功能'
  3332. ).style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3333. input_thanks_follow_periodic_trigger_trigger_num_max = ui.input(
  3334. label='触发次数最大值',
  3335. value=config.get("thanks", "follow", "periodic_trigger", "trigger_num_max"),
  3336. placeholder='周期到后,会触发n次此功能'
  3337. ).style("width:100px;").tooltip('周期到后,会触发n次此功能,次数从最大最小值之间随机生成')
  3338. if config.get("webui", "show_card", "common_config", "choose_song"):
  3339. with ui.card().style(card_css):
  3340. ui.label('点歌模式')
  3341. with ui.row():
  3342. switch_choose_song_enable = ui.switch('启用', value=config.get("choose_song", "enable")).style(switch_internal_css)
  3343. textarea_choose_song_start_cmd = ui.textarea(
  3344. label='点歌触发命令',
  3345. value=textarea_data_change(config.get("choose_song", "start_cmd")),
  3346. placeholder='点歌触发命令,换行分隔,支持多个命令,弹幕发送触发(完全匹配才行)'
  3347. ).style("width:200px;").tooltip('点歌触发命令,换行分隔,支持多个命令,弹幕发送触发(完全匹配才行)')
  3348. textarea_choose_song_stop_cmd = ui.textarea(
  3349. label='取消点歌命令',
  3350. value=textarea_data_change(config.get("choose_song", "stop_cmd")),
  3351. placeholder='停止点歌命令,换行分隔,支持多个命令,弹幕发送触发(完全匹配才行)'
  3352. ).style("width:200px;").tooltip('停止点歌命令,换行分隔,支持多个命令,弹幕发送触发(完全匹配才行)')
  3353. textarea_choose_song_random_cmd = ui.textarea(
  3354. label='随机点歌命令',
  3355. value=textarea_data_change(config.get("choose_song", "random_cmd")),
  3356. placeholder='随机点歌命令,换行分隔,支持多个命令,弹幕发送触发(完全匹配才行)'
  3357. ).style("width:200px;").tooltip('随机点歌命令,换行分隔,支持多个命令,弹幕发送触发(完全匹配才行)')
  3358. with ui.row():
  3359. input_choose_song_song_path = ui.input(
  3360. label='歌曲路径',
  3361. value=config.get("choose_song", "song_path"),
  3362. placeholder='歌曲音频存放的路径,会自动读取音频文件'
  3363. ).style("width:200px;").tooltip('歌曲音频存放的路径,会自动读取音频文件')
  3364. input_choose_song_match_fail_copy = ui.input(
  3365. label='匹配失败文案',
  3366. value=config.get("choose_song", "match_fail_copy"),
  3367. placeholder='匹配失败返回的音频文案 注意 {content} 这个是用于替换用户发送的歌名的,请务必不要乱删!影响使用!'
  3368. ).style("width:300px;").tooltip('匹配失败返回的音频文案 注意 {content} 这个是用于替换用户发送的歌名的,请务必不要乱删!影响使用!')
  3369. input_choose_song_similarity = ui.input(
  3370. label='匹配最低相似度',
  3371. value=config.get("choose_song", "similarity"),
  3372. placeholder='最低音频匹配相似度,就是说用户发送的内容和本地音频库中音频文件名的最低相似度。\n低了就会被当做一般弹幕处理'
  3373. ).style("width:200px;").tooltip('最低音频匹配相似度,就是说用户发送的内容和本地音频库中音频文件名的最低相似度。\n低了就会被当做一般弹幕处理')
  3374. if config.get("webui", "show_card", "common_config", "schedule"):
  3375. with ui.card().style(card_css):
  3376. ui.label('定时任务')
  3377. with ui.row():
  3378. input_schedule_index = ui.input(label='任务索引', value="", placeholder='任务组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  3379. button_schedule_add = ui.button('增加任务组', on_click=schedule_add, color=button_internal_color).style(button_internal_css)
  3380. button_schedule_del = ui.button('删除任务组', on_click=lambda: schedule_del(input_schedule_index.value), color=button_internal_color).style(button_internal_css)
  3381. schedule_var = {}
  3382. schedule_config_card = ui.card()
  3383. for index, schedule in enumerate(config.get("schedule")):
  3384. with schedule_config_card.style(card_css):
  3385. with ui.row():
  3386. schedule_var[str(4 * index)] = ui.switch(text=f"启用任务#{index}", value=schedule["enable"]).style(switch_internal_css)
  3387. schedule_var[str(4 * index + 1)] = ui.input(label=f"最小循环周期#{index}", value=schedule["time_min"], placeholder='定时任务循环的周期最小时长(秒),即每间隔这个周期就会执行一次').style("width:100px;").tooltip('定时任务循环的周期最小时长(秒),最终周期会从最大最小之间随机生成,即每间隔这个周期就会执行一次')
  3388. schedule_var[str(4 * index + 2)] = ui.input(label=f"最大循环周期#{index}", value=schedule["time_max"], placeholder='定时任务循环的周期最大时长(秒),即每间隔这个周期就会执行一次').style("width:100px;").tooltip('定时任务循环的周期最小时长(秒),最终周期会从最大最小之间随机生成,即每间隔这个周期就会执行一次')
  3389. schedule_var[str(4 * index + 3)] = ui.textarea(label=f"文案列表#{index}", value=textarea_data_change(schedule["copy"]), placeholder='存放文案的列表,通过空格或换行分割,通过{变量}来替换关键数据,可修改源码自定义功能').style("width:500px;").tooltip('存放文案的列表,通过空格或换行分割,通过{变量}来替换关键数据,可修改源码自定义功能')
  3390. if config.get("webui", "show_card", "common_config", "idle_time_task"):
  3391. with ui.card().style(card_css):
  3392. ui.label('闲时任务')
  3393. with ui.row():
  3394. switch_idle_time_task_enable = ui.switch('启用', value=config.get("idle_time_task", "enable")).style(switch_internal_css)
  3395. select_idle_time_task_type = ui.select(
  3396. label='机制类型',
  3397. options={
  3398. '待合成消息队列更新闲时': '待合成消息队列更新闲时',
  3399. '待播放音频队列更新闲时': '待播放音频队列更新闲时',
  3400. '直播间无消息更新闲时': '直播间无消息更新闲时',
  3401. },
  3402. value=config.get("idle_time_task", "type")
  3403. ).tooltip('闲时任务执行的逻辑,在不同逻辑下可以实现不同的触发效果。\n如果是用于带货,可以选用 待播放音频队列更新闲时,然后把触发值设为1,从而在音频数少于1的情况下才会触发闲时任务,有效抑制大量任务产生。\n如果用于不需要一直说话的场景,推荐使用:直播间无消息更新闲时,然后把间隔设大点,隔一段时间触发一次。')
  3404. with ui.row():
  3405. input_idle_time_task_idle_min_msg_queue_len_to_trigger = ui.input(
  3406. label='待合成消息队列个数小于此值时触发',
  3407. value=config.get("idle_time_task", "min_msg_queue_len_to_trigger"),
  3408. placeholder='最小闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间'
  3409. ).style("width:250px;").tooltip('最小闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间')
  3410. input_idle_time_task_idle_min_audio_queue_len_to_trigger = ui.input(
  3411. label='待播放音频队列个数小于此值时触发',
  3412. value=config.get("idle_time_task", "min_audio_queue_len_to_trigger"),
  3413. placeholder='最小闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间'
  3414. ).style("width:250px;").tooltip('最小闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间')
  3415. with ui.row():
  3416. input_idle_time_task_idle_time_min = ui.input(
  3417. label='最小闲时时间',
  3418. value=config.get("idle_time_task", "idle_time_min"),
  3419. placeholder='最小闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间'
  3420. ).style("width:150px;").tooltip('最小闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间')
  3421. input_idle_time_task_idle_time_max = ui.input(
  3422. label='最大闲时时间',
  3423. value=config.get("idle_time_task", "idle_time_max"),
  3424. placeholder='最大闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间'
  3425. ).style("width:150px;").tooltip('最大闲时间隔时间(正整数,单位:秒),就是在没有弹幕情况下经过的时间')
  3426. input_idle_time_task_wait_play_audio_num_threshold = ui.input(
  3427. label='等待播放音频数量阈值',
  3428. value=config.get("idle_time_task", "wait_play_audio_num_threshold"),
  3429. placeholder='当等待播放音频数量超过这个阈值,将会在音频播放完毕后触发闲时时间减少到设定的缩减值,旨在控制闲时任务触发总量'
  3430. ).style("width:150px;").tooltip('当等待播放音频数量超过这个阈值,将会在音频播放完毕后触发闲时时间减少到设定的缩减值,旨在控制闲时任务触发总量')
  3431. input_idle_time_task_idle_time_reduce_to = ui.input(label='闲时计时减小到', value=config.get("idle_time_task", "idle_time_reduce_to"), placeholder='达到阈值情况下,闲时计时缩减到的数值').style("width:150px;").tooltip('达到阈值情况下,闲时计时缩减到的数值')
  3432. with ui.row():
  3433. ui.label('刷新闲时计时的消息类型')
  3434. # 类型列表
  3435. idle_time_task_trigger_type_list = ["comment", "gift", "entrance", "follow"]
  3436. idle_time_task_trigger_type_mapping = {
  3437. "comment": "弹幕",
  3438. "gift": "礼物",
  3439. "entrance": "入场",
  3440. "follow": "关注",
  3441. }
  3442. idle_time_task_trigger_type_var = {}
  3443. for index, idle_time_task_trigger_type in enumerate(idle_time_task_trigger_type_list):
  3444. if idle_time_task_trigger_type in config.get("idle_time_task", "trigger_type"):
  3445. idle_time_task_trigger_type_var[str(index)] = ui.checkbox(text=idle_time_task_trigger_type_mapping[idle_time_task_trigger_type], value=True)
  3446. else:
  3447. idle_time_task_trigger_type_var[str(index)] = ui.checkbox(text=idle_time_task_trigger_type_mapping[idle_time_task_trigger_type], value=False)
  3448. with ui.row():
  3449. switch_idle_time_task_copywriting_enable = ui.switch('文案模式', value=config.get("idle_time_task", "copywriting", "enable")).style(switch_internal_css)
  3450. switch_idle_time_task_copywriting_random = ui.switch('随机文案', value=config.get("idle_time_task", "copywriting", "random")).style(switch_internal_css)
  3451. textarea_idle_time_task_copywriting_copy = ui.textarea(
  3452. label='文案列表',
  3453. value=textarea_data_change(config.get("idle_time_task", "copywriting", "copy")),
  3454. placeholder='文案列表,文案之间用换行分隔,文案会丢LLM进行处理后直接合成返回的结果'
  3455. ).style("width:800px;").tooltip('文案列表,文案之间用换行分隔,文案会丢LLM进行处理后直接合成返回的结果')
  3456. with ui.row():
  3457. switch_idle_time_task_comment_enable = ui.switch('弹幕触发LLM模式', value=config.get("idle_time_task", "comment", "enable")).style(switch_internal_css)
  3458. switch_idle_time_task_comment_random = ui.switch('随机弹幕', value=config.get("idle_time_task", "comment", "random")).style(switch_internal_css)
  3459. textarea_idle_time_task_comment_copy = ui.textarea(
  3460. label='弹幕列表',
  3461. value=textarea_data_change(config.get("idle_time_task", "comment", "copy")),
  3462. placeholder='弹幕列表,弹幕之间用换行分隔,文案会丢LLM进行处理后直接合成返回的结果'
  3463. ).style("width:800px;").tooltip('弹幕列表,弹幕之间用换行分隔,文案会丢LLM进行处理后直接合成返回的结果')
  3464. with ui.row():
  3465. switch_idle_time_task_local_audio_enable = ui.switch('本地音频模式', value=config.get("idle_time_task", "local_audio", "enable")).style(switch_internal_css)
  3466. switch_idle_time_task_local_audio_random = ui.switch('随机本地音频', value=config.get("idle_time_task", "local_audio", "random")).style(switch_internal_css)
  3467. textarea_idle_time_task_local_audio_path = ui.textarea(
  3468. label='本地音频路径列表',
  3469. value=textarea_data_change(config.get("idle_time_task", "local_audio", "path")),
  3470. placeholder='本地音频路径列表,相对/绝对路径之间用换行分隔,音频文件会直接丢进音频播放队列'
  3471. ).style("width:800px;").tooltip('本地音频路径列表,相对/绝对路径之间用换行分隔,音频文件会直接丢进音频播放队列')
  3472. if config.get("webui", "show_card", "common_config", "search_online"):
  3473. with ui.card().style(card_css):
  3474. ui.label('联网搜索')
  3475. with ui.row():
  3476. switch_search_online_enable = ui.switch('启用', value=config.get("search_online", "enable")).style(switch_internal_css)
  3477. switch_search_online_keyword_enable = ui.switch('关键词触发', value=config.get("search_online", "keyword_enable")).style(switch_internal_css)
  3478. textarea_search_online_before_keyword = ui.textarea(
  3479. label='关键词前缀',
  3480. placeholder='前缀必须携带其中任一字符串才能触发联网搜索\n例如:配置 【联网:】那么这个会触发:联网:杭州天气',
  3481. value=textarea_data_change(config.get("search_online", "before_keyword"))
  3482. ).style("width:200px;").tooltip('前缀必须携带其中任一字符串才能触发联网搜索\n例如:配置 【联网:】那么这个会触发:联网:杭州天气')
  3483. select_search_online_engine = ui.select(
  3484. label='搜索引擎',
  3485. options={'baidu': '百度搜索', 'google': '谷歌搜索'},
  3486. value=config.get("search_online", "engine")
  3487. ).style("width:100px;").tooltip('使用的搜索引擎')
  3488. input_search_online_engine_id = ui.input(label='引擎ID', value=config.get("search_online", "engine_id"), placeholder='默认:1,不建议修改').style("width:100px;").tooltip('就是单个引擎可能会有多种搜索方式,目前仅谷歌有2种,默认不建议修改')
  3489. input_search_online_count = ui.input(label='检索的文章数', value=config.get("search_online", "count"), placeholder='默认为:1,但有时候可能文章内容解析出来是空的,可以适当扩大检索数').style("width:100px;").tooltip('默认为:1,但有时候可能文章内容解析出来是空的,可以适当扩大检索数')
  3490. input_search_online_resp_template = ui.input(label='结果模板', value=config.get("search_online", "resp_template"), placeholder='检索后数据会以这个模板格式生成问题,请不要删除{}的两个变量,会导致无法运行').style("width:500px;").tooltip('检索后数据会以这个模板格式生成问题,请不要删除{}的两个变量,会导致无法运行')
  3491. input_search_online_http_proxy = ui.input(
  3492. label='HTTP代理地址',
  3493. value=config.get("search_online", "http_proxy"),
  3494. placeholder='http代理地址,需要走代理时配置此项。',
  3495. validation={
  3496. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3497. }
  3498. ).style("width:200px;").tooltip('http代理地址,需要走代理时配置此项。')
  3499. input_search_online_https_proxy = ui.input(
  3500. label='HTTPS代理地址',
  3501. value=config.get("search_online", "https_proxy"),
  3502. placeholder='https代理地址,需要走代理时配置此项。',
  3503. validation={
  3504. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3505. }
  3506. ).style("width:200px;").tooltip('https代理地址,需要走代理时配置此项。')
  3507. if config.get("webui", "show_card", "common_config", "trends_copywriting"):
  3508. with ui.card().style(card_css):
  3509. ui.label('动态文案')
  3510. with ui.row():
  3511. switch_trends_copywriting_enable = ui.switch('启用', value=config.get("trends_copywriting", "enable")).style(switch_internal_css)
  3512. select_trends_copywriting_llm_type = ui.select(
  3513. label='LLM类型',
  3514. options=chat_type_options,
  3515. value=config.get("trends_copywriting", "llm_type")
  3516. ).style("width:200px;")
  3517. switch_trends_copywriting_random_play = ui.switch('随机播放', value=config.get("trends_copywriting", "random_play")).style(switch_internal_css)
  3518. input_trends_copywriting_play_interval = ui.input(label='文案播放间隔', value=config.get("trends_copywriting", "play_interval"), placeholder='文案于文案之间的播放间隔时间(秒)').style("width:200px;").tooltip('文案于文案之间的播放间隔时间(秒)')
  3519. with ui.row():
  3520. input_trends_copywriting_index = ui.input(label='文案索引', value="", placeholder='文案组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数').tooltip('文案组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  3521. button_trends_copywriting_add = ui.button('增加文案组', on_click=trends_copywriting_add, color=button_internal_color).style(button_internal_css)
  3522. button_trends_copywriting_del = ui.button('删除文案组', on_click=lambda: trends_copywriting_del(input_trends_copywriting_index.value), color=button_internal_color).style(button_internal_css)
  3523. trends_copywriting_copywriting_var = {}
  3524. trends_copywriting_config_card = ui.card()
  3525. for index, trends_copywriting_copywriting in enumerate(config.get("trends_copywriting", "copywriting")):
  3526. with trends_copywriting_config_card.style(card_css):
  3527. with ui.row():
  3528. trends_copywriting_copywriting_var[str(3 * index)] = ui.input(label=f"文案路径#{index + 1}", value=trends_copywriting_copywriting["folder_path"], placeholder='文案文件存储的文件夹路径').style("width:200px;").tooltip('文案文件存储的文件夹路径')
  3529. trends_copywriting_copywriting_var[str(3 * index + 1)] = ui.switch(text=f"提示词转换#{index + 1}", value=trends_copywriting_copywriting["prompt_change_enable"])
  3530. trends_copywriting_copywriting_var[str(3 * index + 2)] = ui.input(label=f"提示词转换内容#{index + 1}", value=trends_copywriting_copywriting["prompt_change_content"], placeholder='使用此提示词内容对文案内容进行转换后再进行合成,使用的LLM为聊天类型配置').style("width:500px;").tooltip('使用此提示词内容对文案内容进行转换后再进行合成,使用的LLM为聊天类型配置')
  3531. if config.get("webui", "show_card", "common_config", "key_mapping"):
  3532. with ui.card().style(card_css):
  3533. ui.label('按键/文案/音频/串口/图片 映射')
  3534. with ui.row():
  3535. switch_key_mapping_enable = ui.switch('启用', value=config.get("key_mapping", "enable")).style(switch_internal_css)
  3536. input_key_mapping_start_cmd = ui.input(
  3537. label='命令前缀',
  3538. value=config.get("key_mapping", "start_cmd"),
  3539. placeholder='想要触发此功能必须以这个字符串做为命令起始,不然将不会被解析为按键映射命令'
  3540. ).style("width:200px;").tooltip('想要触发此功能必须以这个字符串做为命令起始,不然将不会被解析为按键映射命令')
  3541. select_key_mapping_type = ui.select(
  3542. label='捕获类型',
  3543. options={'弹幕': '弹幕', '回复': '回复', '弹幕+回复': '弹幕+回复'},
  3544. value=config.get("key_mapping", "type")
  3545. ).style("width:200px").tooltip('什么类型的数据会触发这个板块的功能')
  3546. with ui.row():
  3547. select_key_mapping_key_trigger_type = ui.select(
  3548. label='按键触发类型',
  3549. options={'不启用': '不启用', '关键词': '关键词', '礼物': '礼物', '关键词+礼物': '关键词+礼物'},
  3550. value=config.get("key_mapping", "key_trigger_type")
  3551. ).style("width:120px").tooltip('什么类型的数据会触发按键映射')
  3552. switch_key_mapping_key_single_sentence_trigger_once_enable = ui.switch('单句仅触发一次(按键)', value=config.get("key_mapping", "key_single_sentence_trigger_once")).style(switch_internal_css).tooltip('一句话的数据,是否只让这句话触发一次按键映射,因为一句话中可能会有多个关键词,触发多次')
  3553. select_key_mapping_copywriting_trigger_type = ui.select(
  3554. label='文案触发类型',
  3555. options={'不启用': '不启用', '关键词': '关键词', '礼物': '礼物', '关键词+礼物': '关键词+礼物'},
  3556. value=config.get("key_mapping", "copywriting_trigger_type")
  3557. ).style("width:120px").tooltip('什么类型的数据会触发文案映射')
  3558. switch_key_mapping_copywriting_single_sentence_trigger_once_enable = ui.switch('单句仅触发一次(文案)', value=config.get("key_mapping", "copywriting_single_sentence_trigger_once")).style(switch_internal_css).tooltip('一句话的数据,是否只让这句话触发一次文案映射,因为一句话中可能会有多个关键词,触发多次')
  3559. select_key_mapping_local_audio_trigger_type = ui.select(
  3560. label='本地音频触发类型',
  3561. options={'不启用': '不启用', '关键词': '关键词', '礼物': '礼物', '关键词+礼物': '关键词+礼物'},
  3562. value=config.get("key_mapping", "local_audio_trigger_type")
  3563. ).style("width:120px").tooltip('什么类型的数据会触发本地音频映射')
  3564. switch_key_mapping_local_audio_single_sentence_trigger_once_enable = ui.switch('单句仅触发一次(文案)', value=config.get("key_mapping", "local_audio_single_sentence_trigger_once")).style(switch_internal_css).tooltip('一句话的数据,是否只让这句话触发一次本地音频映射,因为一句话中可能会有多个关键词,触发多次')
  3565. select_key_mapping_serial_trigger_type = ui.select(
  3566. label='串口触发类型',
  3567. options={'不启用': '不启用', '关键词': '关键词', '礼物': '礼物', '关键词+礼物': '关键词+礼物'},
  3568. value=config.get("key_mapping", "serial_trigger_type")
  3569. ).style("width:120px").tooltip('什么类型的数据会触发文案映射')
  3570. switch_key_mapping_serial_single_sentence_trigger_once_enable = ui.switch('单句仅触发一次(串口)', value=config.get("key_mapping", "serial_single_sentence_trigger_once")).style(switch_internal_css).tooltip('一句话的数据,是否只让这句话触发一次文案映射,因为一句话中可能会有多个关键词,触发多次')
  3571. select_key_mapping_img_path_trigger_type = ui.select(
  3572. label='图片触发类型',
  3573. options={'不启用': '不启用', '关键词': '关键词', '礼物': '礼物', '关键词+礼物': '关键词+礼物'},
  3574. value=config.get("key_mapping", "img_path_trigger_type")
  3575. ).style("width:120px").tooltip('什么类型的数据会触发文案映射')
  3576. switch_key_mapping_img_path_single_sentence_trigger_once_enable = ui.switch('单句仅触发一次(图片显示)', value=config.get("key_mapping", "img_path_single_sentence_trigger_once")).style(switch_internal_css).tooltip('一句话的数据,是否只让这句话触发一次文案映射,因为一句话中可能会有多个关键词,触发多次')
  3577. with ui.row():
  3578. input_key_mapping_index = ui.input(label='配置索引', value="", placeholder='配置组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数').tooltip('配置组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  3579. button_key_mapping_add = ui.button('增加配置组', on_click=key_mapping_add, color=button_internal_color).style(button_internal_css)
  3580. button_key_mapping_del = ui.button('删除配置组', on_click=lambda: key_mapping_del(input_key_mapping_index.value), color=button_internal_color).style(button_internal_css)
  3581. key_mapping_config_var = {}
  3582. key_mapping_config_card = ui.card()
  3583. for index, key_mapping_config in enumerate(config.get("key_mapping", "config")):
  3584. with key_mapping_config_card.style(card_css):
  3585. with ui.row():
  3586. num = 9
  3587. key_mapping_config_var[str(num * index)] = ui.textarea(label=f"关键词#{index + 1}", value=textarea_data_change(key_mapping_config["keywords"]), placeholder='此处输入触发的关键词,多个请以换行分隔').style("width:100px;").tooltip('此处输入触发的关键词,多个请以换行分隔')
  3588. key_mapping_config_var[str(num * index + 1)] = ui.textarea(label=f"礼物#{index + 1}", value=textarea_data_change(key_mapping_config["gift"]), placeholder='此处输入触发的礼物名,多个请以换行分隔').style("width:100px;").tooltip('此处输入触发的礼物名,多个请以换行分隔')
  3589. key_mapping_config_var[str(num * index + 2)] = ui.textarea(label=f"按键#{index + 1}", value=textarea_data_change(key_mapping_config["keys"]), placeholder='此处输入你要映射的按键,多个按键请以换行分隔(按键名参考pyautogui规则)').style("width:100px;").tooltip('此处输入你要映射的按键,多个按键请以换行分隔(按键名参考pyautogui规则)')
  3590. key_mapping_config_var[str(num * index + 3)] = ui.input(label=f"相似度#{index + 1}", value=key_mapping_config["similarity"], placeholder='关键词与用户输入的相似度,默认1即100%').style("width:50px;").tooltip('关键词与用户输入的相似度,默认1即100%')
  3591. key_mapping_config_var[str(num * index + 4)] = ui.textarea(label=f"文案#{index + 1}", value=textarea_data_change(key_mapping_config["copywriting"]), placeholder='此处输入触发后合成的文案内容,多个请以换行分隔').style("width:300px;").tooltip('此处输入触发后合成的文案内容,多个请以换行分隔')
  3592. key_mapping_config_var[str(num * index + 5)] = ui.textarea(label=f"本地音频#{index + 1}", value=textarea_data_change(key_mapping_config["local_audio"]), placeholder='此处输入触发后播放的本地音频路径,多个请以换行分隔').style("width:300px;").tooltip('此处输入触发后播放的本地音频路径,多个请以换行分隔')
  3593. key_mapping_config_var[str(num * index + 6)] = ui.input(label=f"串口名#{index + 1}", value=key_mapping_config["serial_name"], placeholder='例如:COM1').style("width:100px;").tooltip('串口页配置的串口名,例如:COM1')
  3594. key_mapping_config_var[str(num * index + 7)] = ui.textarea(label=f"串口发送内容#{index + 1}", value=textarea_data_change(key_mapping_config["serial_send_data"]), placeholder='多个请以换行分隔,ASCII例如:open led\nHEX例如(2个字符的十六进制字符):313233').style("width:300px;").tooltip('此处输入发送到串口的数据内容,数据类型根据串口页设置决定,多个请以换行分隔')
  3595. key_mapping_config_var[str(num * index + 8)] = ui.textarea(label=f"图片路径#{index + 1}", value=textarea_data_change(key_mapping_config["img_path"]), placeholder='多个请以换行分隔,支持绝对路径或相对路径,需要注意路径的斜杠哈').style("width:300px;").tooltip('此处输入图片路径,多个请以换行分隔。默认随机显示')
  3596. # with ui.card().style(card_css):
  3597. # key_mapping_config_var[str(6 * index + 5)] = ui.textarea(label=f"串口号#{index + 1}", value=textarea_data_change(key_mapping_config["serial_name"]), placeholder='例如:COM1').style("width:100px;").tooltip('发送的串口名')
  3598. # key_mapping_config_var[str(6 * index + 5)] = ui.textarea(label=f"发送数据#{index + 1}", value=key_mapping_config["serial_data"], placeholder='根据类型填写,多个请以换行分隔').style("width:300px;").tooltip('根据类型填写,多个请以换行分隔')
  3599. with ui.expansion('辅助软件对接', icon="extension", value=True).classes('w-full'):
  3600. if config.get("webui", "show_card", "common_config", "sd"):
  3601. with ui.card().style(card_css):
  3602. ui.label('Stable Diffusion')
  3603. with ui.row():
  3604. switch_sd_enable = ui.switch('启用', value=config.get("sd", "enable")).style(switch_internal_css)
  3605. select_sd_translate_type = ui.select(
  3606. label='翻译类型',
  3607. options={'none': '不启用', 'baidu': '百度翻译', 'google': '谷歌翻译'},
  3608. value=config.get("sd", "translate_type")
  3609. ).style("width:100px;").tooltip('针对触发的画图命令使用翻译页的配置,进行翻译后再丢给SD画图')
  3610. select_sd_prompt_llm_type = ui.select(
  3611. label='LLM类型',
  3612. options=chat_type_options,
  3613. value=config.get("sd", "prompt_llm", "type")
  3614. ).style("width:100px;")
  3615. input_sd_prompt_llm_before_prompt = ui.input(label='提示词前缀', value=config.get("sd", "prompt_llm", "before_prompt"), placeholder='LLM提示词前缀').style("width:300px;")
  3616. input_sd_prompt_llm_after_prompt = ui.input(label='提示词后缀', value=config.get("sd", "prompt_llm", "after_prompt"), placeholder='LLM提示词后缀').style("width:300px;")
  3617. with ui.row():
  3618. input_sd_trigger = ui.input(label='弹幕触发前缀', value=config.get("sd", "trigger"), placeholder='触发的关键词(弹幕头部触发)').style("width:200px;")
  3619. input_sd_ip = ui.input(label='IP地址', value=config.get("sd", "ip"), placeholder='服务运行的IP地址').style("width:200px;")
  3620. input_sd_port = ui.input(label='端口', value=config.get("sd", "port"), placeholder='服务运行的端口').style("width:100px;")
  3621. input_sd_negative_prompt = ui.input(label='负面提示词', value=config.get("sd", "negative_prompt"), placeholder='负面文本提示,用于指定与生成图像相矛盾或相反的内容').style("width:200px;")
  3622. input_sd_seed = ui.input(label='随机种子', value=config.get("sd", "seed"), placeholder='随机种子,用于控制生成过程的随机性。可以设置一个整数值,以获得可重复的结果。').style("width:100px;")
  3623. textarea_sd_styles = ui.textarea(label='图像风格', placeholder='样式列表,用于指定生成图像的风格。可以包含多个风格,例如 ["anime", "portrait"]', value=textarea_data_change(config.get("sd", "styles"))).style("width:200px;")
  3624. with ui.row():
  3625. input_sd_cfg_scale = ui.input(label='提示词相关性', value=config.get("sd", "cfg_scale"), placeholder='提示词相关性,无分类器指导信息影响尺度(Classifier Free Guidance Scale) -图像应在多大程度上服从提示词-较低的值会产生更有创意的结果。').style("width:100px;")
  3626. input_sd_steps = ui.input(label='生成图像步数', value=config.get("sd", "steps"), placeholder='生成图像的步数,用于控制生成的精确程度。').style("width:100px;")
  3627. input_sd_hr_resize_x = ui.input(label='图像水平像素', value=config.get("sd", "hr_resize_x"), placeholder='生成图像的水平尺寸。').style("width:100px;")
  3628. input_sd_hr_resize_y = ui.input(label='图像垂直像素', value=config.get("sd", "hr_resize_y"), placeholder='生成图像的垂直尺寸。').style("width:100px;")
  3629. input_sd_denoising_strength = ui.input(label='去噪强度', value=config.get("sd", "denoising_strength"), placeholder='去噪强度,用于控制生成图像中的噪点。').style("width:100px;")
  3630. with ui.row():
  3631. switch_sd_enable_hr = ui.switch('高分辨率生成', value=config.get("sd", "enable_hr")).style(switch_internal_css)
  3632. input_sd_hr_scale = ui.input(label='高分辨率缩放因子', value=config.get("sd", "hr_scale"), placeholder='高分辨率缩放因子,用于指定生成图像的高分辨率缩放级别。').style("width:200px;")
  3633. input_sd_hr_second_pass_steps = ui.input(label='高分生二次传递步数', value=config.get("sd", "hr_second_pass_steps"), placeholder='高分辨率生成的第二次传递步数。').style("width:200px;")
  3634. switch_sd_save_enable = ui.switch('保存图片到本地', value=config.get("sd", "save_enable")).style(switch_internal_css)
  3635. switch_sd_loop_cover = ui.switch('本地图片循环覆盖', value=config.get("sd", "loop_cover")).style(switch_internal_css)
  3636. input_sd_save_path = ui.input(label='图片保存路径', value=config.get("sd", "save_path"), placeholder='生成图片存储路径,不建议修改').style("width:200px;")
  3637. if config.get("webui", "show_card", "common_config", "web_captions_printer"):
  3638. with ui.card().style(card_css):
  3639. ui.label('web字幕打印机')
  3640. with ui.grid(columns=2):
  3641. switch_web_captions_printer_enable = ui.switch('启用', value=config.get("web_captions_printer", "enable")).style(switch_internal_css).tooltip("如果您使用了audio player来做音频播放,并开启了其web字幕打印机功能,\n那请勿启动此功能,因为这样就重复惹")
  3642. input_web_captions_printer_api_ip_port = ui.input(
  3643. label='API地址',
  3644. value=config.get("web_captions_printer", "api_ip_port"),
  3645. placeholder='web字幕打印机的API地址,只需要 http://ip:端口 即可',
  3646. validation={
  3647. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3648. }
  3649. ).style("width:200px;").tooltip('web字幕打印机的API地址,只需要 http://ip:端口 即可')
  3650. with ui.expansion('高级功能', icon="view_in_ar", value=True).classes('w-full'):
  3651. if config.get("webui", "show_card", "common_config", "log"):
  3652. with ui.card().style(card_css):
  3653. ui.label('日志')
  3654. with ui.grid(columns=4):
  3655. switch_captions_enable = ui.switch('启用', value=config.get("captions", "enable")).style(switch_internal_css)
  3656. select_comment_log_type = ui.select(
  3657. label='弹幕日志类型',
  3658. options={'问答': '问答', '问题': '问题', '回答': '回答', '不记录': '不记录'},
  3659. value=config.get("comment_log_type")
  3660. )
  3661. input_captions_file_path = ui.input(label='字幕日志路径', value=config.get("captions", "file_path"), placeholder='字幕日志存储路径').style("width:200px;")
  3662. input_captions_raw_file_path = ui.input(label='原文字幕日志路径', placeholder='原文字幕日志存储路径',
  3663. value=config.get("captions", "raw_file_path")).style("width:200px;")
  3664. if config.get("webui", "show_card", "common_config", "database"):
  3665. with ui.card().style(card_css):
  3666. ui.label('数据库')
  3667. with ui.grid(columns=4):
  3668. switch_database_comment_enable = ui.switch('弹幕日志', value=config.get("database", "comment_enable")).style(switch_internal_css)
  3669. switch_database_entrance_enable = ui.switch('入场日志', value=config.get("database", "entrance_enable")).style(switch_internal_css)
  3670. switch_database_gift_enable = ui.switch('礼物日志', value=config.get("database", "gift_enable")).style(switch_internal_css)
  3671. input_database_path = ui.input(label='数据库路径', value=config.get("database", "path"), placeholder='数据库文件存储路径').style("width:200px;")
  3672. if config.get("webui", "show_card", "common_config", "custom_cmd"):
  3673. with ui.card().style(card_css):
  3674. ui.label('自定义命令')
  3675. with ui.row():
  3676. switch_custom_cmd_enable = ui.switch('启用', value=config.get("custom_cmd", "enable")).style(switch_internal_css)
  3677. select_custom_cmd_type = ui.select(
  3678. label='类型',
  3679. options={'弹幕': '弹幕'},
  3680. value=config.get("custom_cmd", "type")
  3681. ).style("width:200px")
  3682. with ui.row():
  3683. input_custom_cmd_index = ui.input(label='配置索引', value="", placeholder='配置组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  3684. button_custom_cmd_add = ui.button('增加配置组', on_click=custom_cmd_add, color=button_internal_color).style(button_internal_css)
  3685. button_custom_cmd_del = ui.button('删除配置组', on_click=lambda: custom_cmd_del(input_custom_cmd_index.value), color=button_internal_color).style(button_internal_css)
  3686. custom_cmd_config_var = {}
  3687. custom_cmd_config_card = ui.card()
  3688. for index, custom_cmd_config in enumerate(config.get("custom_cmd", "config")):
  3689. with custom_cmd_config_card.style(card_css):
  3690. with ui.row():
  3691. custom_cmd_config_var[str(7 * index)] = ui.textarea(label=f"关键词#{index + 1}", value=textarea_data_change(custom_cmd_config["keywords"]), placeholder='此处输入触发的关键词,多个请以换行分隔').style("width:200px;")
  3692. custom_cmd_config_var[str(7 * index + 1)] = ui.input(label=f"相似度#{index + 1}", value=custom_cmd_config["similarity"], placeholder='关键词与用户输入的相似度,默认1即100%').style("width:100px;")
  3693. custom_cmd_config_var[str(7 * index + 2)] = ui.textarea(
  3694. label=f"API URL#{index + 1}",
  3695. value=custom_cmd_config["api_url"],
  3696. placeholder='发送HTTP请求的API链接',
  3697. validation={
  3698. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3699. }
  3700. ).style("width:300px;").tooltip('发送HTTP请求的API链接')
  3701. custom_cmd_config_var[str(7 * index + 3)] = ui.select(label=f"API类型#{index + 1}", value=custom_cmd_config["api_type"], options={"GET": "GET"}).style("width:100px;")
  3702. custom_cmd_config_var[str(7 * index + 4)] = ui.select(label=f"请求返回数据类型#{index + 1}", value=custom_cmd_config["resp_data_type"], options={"json": "json", "content": "content"}).style("width:150px;")
  3703. custom_cmd_config_var[str(7 * index + 5)] = ui.textarea(label=f"数据解析(eval执行)#{index + 1}", value=custom_cmd_config["data_analysis"], placeholder='数据解析,请不要随意修改resp变量,会被用于最后返回数据内容的解析').style("width:200px;").tooltip('数据解析,请不要随意修改resp变量,会被用于最后返回数据内容的解析')
  3704. custom_cmd_config_var[str(7 * index + 6)] = ui.textarea(label=f"返回内容模板#{index + 1}", value=custom_cmd_config["resp_template"], placeholder='请不要随意删除data变量,支持动态变量,最终会合并成完成内容进行音频合成').style("width:300px;").tooltip("请不要随意删除data变量,支持动态变量,最终会合并成完成内容进行音频合成")
  3705. if config.get("webui", "show_card", "common_config", "trends_config"):
  3706. with ui.card().style(card_css):
  3707. ui.label('动态配置')
  3708. with ui.row():
  3709. switch_trends_config_enable = ui.switch('启用', value=config.get("trends_config", "enable")).style(switch_internal_css)
  3710. trends_config_path_var = {}
  3711. for index, trends_config_path in enumerate(config.get("trends_config", "path")):
  3712. with ui.grid(columns=2):
  3713. trends_config_path_var[str(2 * index)] = ui.input(label="在线人数范围", value=trends_config_path["online_num"], placeholder='在线人数范围,用减号-分隔,例如:0-10').style("width:200px;").tooltip("在线人数范围,用减号-分隔,例如:0-10")
  3714. trends_config_path_var[str(2 * index + 1)] = ui.input(label="配置路径", value=trends_config_path["path"], placeholder='此处输入加载的配置文件的路径').style("width:200px;").tooltip("此处输入加载的配置文件的路径")
  3715. if config.get("webui", "show_card", "common_config", "abnormal_alarm"):
  3716. with ui.card().style(card_css):
  3717. ui.label('异常报警')
  3718. with ui.row():
  3719. switch_abnormal_alarm_platform_enable = ui.switch('启用平台报警', value=config.get("abnormal_alarm", "platform", "enable")).style(switch_internal_css)
  3720. select_abnormal_alarm_platform_type = ui.select(
  3721. label='类型',
  3722. options={'local_audio': '本地音频'},
  3723. value=config.get("abnormal_alarm", "platform", "type")
  3724. )
  3725. input_abnormal_alarm_platform_start_alarm_error_num = ui.input(label='开始报警错误数', value=config.get("abnormal_alarm", "platform", "start_alarm_error_num"), placeholder='开始异常报警的错误数,超过这个数后就会报警').style("width:100px;")
  3726. input_abnormal_alarm_platform_auto_restart_error_num = ui.input(label='自动重启错误数', value=config.get("abnormal_alarm", "platform", "auto_restart_error_num"), placeholder='记得先启用“自动运行”功能。自动重启的错误数,超过这个数后就会自动重启webui。').style("width:100px;")
  3727. input_abnormal_alarm_platform_local_audio_path = ui.input(label='本地音频路径', value=config.get("abnormal_alarm", "platform", "local_audio_path"), placeholder='本地音频存储的文件路径(可以是多个音频,随机一个)').style("width:300px;")
  3728. with ui.row():
  3729. switch_abnormal_alarm_llm_enable = ui.switch('启用LLM报警', value=config.get("abnormal_alarm", "llm", "enable")).style(switch_internal_css)
  3730. select_abnormal_alarm_llm_type = ui.select(
  3731. label='类型',
  3732. options={'local_audio': '本地音频'},
  3733. value=config.get("abnormal_alarm", "llm", "type")
  3734. )
  3735. input_abnormal_alarm_llm_start_alarm_error_num = ui.input(label='开始报警错误数', value=config.get("abnormal_alarm", "llm", "start_alarm_error_num"), placeholder='开始异常报警的错误数,超过这个数后就会报警').style("width:100px;")
  3736. input_abnormal_alarm_llm_auto_restart_error_num = ui.input(label='自动重启错误数', value=config.get("abnormal_alarm", "llm", "auto_restart_error_num"), placeholder='记得先启用“自动运行”功能。自动重启的错误数,超过这个数后就会自动重启webui。').style("width:100px;")
  3737. input_abnormal_alarm_llm_local_audio_path = ui.input(label='本地音频路径', value=config.get("abnormal_alarm", "llm", "local_audio_path"), placeholder='本地音频存储的文件路径(可以是多个音频,随机一个)').style("width:300px;")
  3738. with ui.row():
  3739. switch_abnormal_alarm_tts_enable = ui.switch('启用TTS报警', value=config.get("abnormal_alarm", "tts", "enable")).style(switch_internal_css)
  3740. select_abnormal_alarm_tts_type = ui.select(
  3741. label='类型',
  3742. options={'local_audio': '本地音频'},
  3743. value=config.get("abnormal_alarm", "tts", "type")
  3744. )
  3745. input_abnormal_alarm_tts_start_alarm_error_num = ui.input(label='开始报警错误数', value=config.get("abnormal_alarm", "tts", "start_alarm_error_num"), placeholder='开始异常报警的错误数,超过这个数后就会报警').style("width:100px;")
  3746. input_abnormal_alarm_tts_auto_restart_error_num = ui.input(label='自动重启错误数', value=config.get("abnormal_alarm", "tts", "auto_restart_error_num"), placeholder='记得先启用“自动运行”功能。自动重启的错误数,超过这个数后就会自动重启webui。').style("width:100px;")
  3747. input_abnormal_alarm_tts_local_audio_path = ui.input(label='本地音频路径', value=config.get("abnormal_alarm", "tts", "local_audio_path"), placeholder='本地音频存储的文件路径(可以是多个音频,随机一个)').style("width:300px;")
  3748. with ui.row():
  3749. switch_abnormal_alarm_svc_enable = ui.switch('启用SVC报警', value=config.get("abnormal_alarm", "svc", "enable")).style(switch_internal_css)
  3750. select_abnormal_alarm_svc_type = ui.select(
  3751. label='类型',
  3752. options={'local_audio': '本地音频'},
  3753. value=config.get("abnormal_alarm", "svc", "type")
  3754. )
  3755. input_abnormal_alarm_svc_start_alarm_error_num = ui.input(label='开始报警错误数', value=config.get("abnormal_alarm", "svc", "start_alarm_error_num"), placeholder='开始异常报警的错误数,超过这个数后就会报警').style("width:100px;")
  3756. input_abnormal_alarm_svc_auto_restart_error_num = ui.input(label='自动重启错误数', value=config.get("abnormal_alarm", "svc", "auto_restart_error_num"), placeholder='记得先启用“自动运行”功能。自动重启的错误数,超过这个数后就会自动重启webui。').style("width:100px;")
  3757. input_abnormal_alarm_svc_local_audio_path = ui.input(label='本地音频路径', value=config.get("abnormal_alarm", "svc", "local_audio_path"), placeholder='本地音频存储的文件路径(可以是多个音频,随机一个)').style("width:300px;")
  3758. with ui.row():
  3759. switch_abnormal_alarm_visual_body_enable = ui.switch('启用虚拟身体报警', value=config.get("abnormal_alarm", "visual_body", "enable")).style(switch_internal_css)
  3760. select_abnormal_alarm_visual_body_type = ui.select(
  3761. label='类型',
  3762. options={'local_audio': '本地音频'},
  3763. value=config.get("abnormal_alarm", "visual_body", "type")
  3764. )
  3765. input_abnormal_alarm_visual_body_start_alarm_error_num = ui.input(label='开始报警错误数', value=config.get("abnormal_alarm", "visual_body", "start_alarm_error_num"), placeholder='开始异常报警的错误数,超过这个数后就会报警').style("width:100px;")
  3766. input_abnormal_alarm_visual_body_auto_restart_error_num = ui.input(label='自动重启错误数', value=config.get("abnormal_alarm", "visual_body", "auto_restart_error_num"), placeholder='记得先启用“自动运行”功能。自动重启的错误数,超过这个数后就会自动重启webui。').style("width:100px;")
  3767. input_abnormal_alarm_visual_body_local_audio_path = ui.input(label='本地音频路径', value=config.get("abnormal_alarm", "visual_body", "local_audio_path"), placeholder='本地音频存储的文件路径(可以是多个音频,随机一个)').style("width:300px;")
  3768. with ui.row():
  3769. switch_abnormal_alarm_other_enable = ui.switch('启用其他报警', value=config.get("abnormal_alarm", "other", "enable")).style(switch_internal_css)
  3770. select_abnormal_alarm_other_type = ui.select(
  3771. label='类型',
  3772. options={'local_audio': '本地音频'},
  3773. value=config.get("abnormal_alarm", "other", "type")
  3774. )
  3775. input_abnormal_alarm_other_start_alarm_error_num = ui.input(label='开始报警错误数', value=config.get("abnormal_alarm", "other", "start_alarm_error_num"), placeholder='开始异常报警的错误数,超过这个数后就会报警').style("width:100px;")
  3776. input_abnormal_alarm_other_auto_restart_error_num = ui.input(label='自动重启错误数', value=config.get("abnormal_alarm", "other", "auto_restart_error_num"), placeholder='记得先启用“自动运行”功能。自动重启的错误数,超过这个数后就会自动重启webui。').style("width:100px;")
  3777. input_abnormal_alarm_other_local_audio_path = ui.input(label='本地音频路径', value=config.get("abnormal_alarm", "other", "local_audio_path"), placeholder='本地音频存储的文件路径(可以是多个音频,随机一个)').style("width:300px;")
  3778. if config.get("webui", "show_card", "common_config", "coordination_program"):
  3779. with ui.expansion('联动程序', icon="settings", value=True).classes('w-full'):
  3780. with ui.row():
  3781. input_coordination_program_index = ui.input(label='配置索引', value="", placeholder='配置组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  3782. button_coordination_program_add = ui.button('增加配置组', on_click=coordination_program_add, color=button_internal_color).style(button_internal_css)
  3783. button_coordination_program_del = ui.button('删除配置组', on_click=lambda: coordination_program_del(input_coordination_program_index.value), color=button_internal_color).style(button_internal_css)
  3784. coordination_program_var = {}
  3785. coordination_program_config_card = ui.card()
  3786. for index, coordination_program in enumerate(config.get("coordination_program")):
  3787. with coordination_program_config_card.style(card_css):
  3788. with ui.row():
  3789. coordination_program_var[str(4 * index)] = ui.switch(f'启用#{index + 1}', value=coordination_program["enable"]).style(switch_internal_css)
  3790. coordination_program_var[str(4 * index + 1)] = ui.input(label=f"程序名#{index + 1}", value=coordination_program["name"], placeholder='给你的程序取个名字,别整特殊符号!').style("width:200px;")
  3791. coordination_program_var[str(4 * index + 2)] = ui.input(label=f"可执行程序#{index + 1}", value=coordination_program["executable"], placeholder='可执行程序的路径,最好是绝对路径,如python的程序').style("width:400px;")
  3792. coordination_program_var[str(4 * index + 3)] = ui.textarea(label=f'参数#{index + 1}', value=textarea_data_change(coordination_program["parameters"]), placeholder='参数,可以传入多个参数,换行分隔。如启动的程序的路径,命令携带的传参等').style("width:500px;")
  3793. with ui.tab_panel(llm_page).style(tab_panel_css):
  3794. if config.get("webui", "show_card", "llm", "chatgpt"):
  3795. with ui.card().style(card_css):
  3796. ui.label("ChatGPT | 闻达 | ChatGLM3 | Kimi Chat | Ollama | One-API等OpenAI接口模型 ")
  3797. with ui.row():
  3798. input_openai_api = ui.input(
  3799. label='API地址',
  3800. placeholder='API请求地址,支持代理',
  3801. value=config.get("openai", "api"),
  3802. validation={
  3803. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3804. }
  3805. ).style("width:200px;")
  3806. textarea_openai_api_key = ui.textarea(label='API密钥', placeholder='API KEY,支持代理', value=textarea_data_change(config.get("openai", "api_key"))).style("width:400px;")
  3807. button_openai_test = ui.button('测试', on_click=lambda: test_openai_key(), color=button_bottom_color).style(button_bottom_css)
  3808. with ui.row():
  3809. chatgpt_models = [
  3810. "gpt-3.5-turbo",
  3811. "gpt-3.5-turbo-instruct",
  3812. "gpt-3.5-turbo-0125",
  3813. "gpt-4",
  3814. "gpt-4-turbo-preview",
  3815. "gpt-4-0125-preview",
  3816. "gpt-4o",
  3817. "gpt-4o-mini",
  3818. "text-embedding-3-large",
  3819. "text-embedding-3-small",
  3820. "text-davinci-003",
  3821. "rwkv",
  3822. "chatglm3-6b",
  3823. "moonshot-v1-8k",
  3824. "gemma:2b",
  3825. "qwen",
  3826. "qwen:1.8b-chat"
  3827. ]
  3828. # 将用户配置的值插入list(如果不存在)
  3829. if config.get("chatgpt", "model") not in chatgpt_models:
  3830. chatgpt_models.append(config.get("chatgpt", "model"))
  3831. data_json = {}
  3832. for line in chatgpt_models:
  3833. data_json[line] = line
  3834. select_chatgpt_model = ui.select(
  3835. label='模型',
  3836. options=data_json,
  3837. value=config.get("chatgpt", "model"),
  3838. with_input=True,
  3839. new_value_mode='add-unique',
  3840. clearable=True
  3841. ).tooltip("如果你没有在此找到你用的模型名,你可以删除此配置项的内容,然后手动输入,最后一定要回车!确认!")
  3842. input_chatgpt_temperature = ui.input(label='温度', placeholder='控制生成文本的随机性。较高的温度值会使生成的文本更随机和多样化,而较低的温度值会使生成的文本更加确定和一致。', value=config.get("chatgpt", "temperature")).style("width:100px;")
  3843. input_chatgpt_max_tokens = ui.input(label='最大token数', placeholder='限制生成回答的最大长度。', value=config.get("chatgpt", "max_tokens")).style("width:100px;")
  3844. input_chatgpt_top_p = ui.input(label='top_p', placeholder='Nucleus采样。这个参数控制模型从累积概率大于一定阈值的令牌中进行采样。较高的值会产生更多的多样性,较低的值会产生更少但更确定的回答。', value=config.get("chatgpt", "top_p")).style("width:100px;")
  3845. switch_chatgpt_stream = ui.switch('流式输出', value=config.get("chatgpt", "stream")).tooltip("是否开启流式输出,开启后,回答会逐句输出,关闭后,回答会一次性输出。")
  3846. with ui.row():
  3847. input_chatgpt_presence_penalty = ui.input(label='存在惩罚', placeholder='控制模型生成回答时对给定问题提示的关注程度。较高的存在惩罚值会减少模型对给定提示的重复程度,鼓励模型更自主地生成回答。', value=config.get("chatgpt", "presence_penalty")).style("width:100px;")
  3848. input_chatgpt_frequency_penalty = ui.input(label='频率惩罚', placeholder='控制生成回答时对已经出现过的令牌的惩罚程度。较高的频率惩罚值会减少模型生成已经频繁出现的令牌,以避免重复和过度使用特定词语。', value=config.get("chatgpt", "frequency_penalty")).style("width:100px;")
  3849. input_chatgpt_preset = ui.input(label='预设', placeholder='用于指定一组预定义的设置,以便模型更好地适应特定的对话场景。', value=config.get("chatgpt", "preset")).style("width:500px")
  3850. if config.get("webui", "show_card", "llm", "claude"):
  3851. with ui.card().style(card_css):
  3852. with ui.card().style(card_css):
  3853. ui.label("Claude")
  3854. with ui.row():
  3855. input_claude_slack_user_token = ui.input(label='slack_user_token', placeholder='Slack平台配置的用户Token,参考文档的Claude板块进行配置', value=config.get("claude", "slack_user_token"))
  3856. input_claude_slack_user_token.style("width:400px")
  3857. input_claude_bot_user_id = ui.input(label='bot_user_id', placeholder='Slack平台添加的Claude显示的成员ID,参考文档的Claude板块进行配置', value=config.get("claude", "bot_user_id"))
  3858. input_claude_slack_user_token.style("width:400px")
  3859. with ui.card().style(card_css):
  3860. ui.label("Claude2")
  3861. with ui.row():
  3862. input_claude2_cookie = ui.input(label='cookie', placeholder='claude.ai官网,打开F12,随便提问抓个包,请求头cookie配置于此', value=config.get("claude2", "cookie"))
  3863. input_claude2_cookie.style("width:400px")
  3864. switch_claude2_use_proxy = ui.switch('启用代理', value=config.get("claude2", "use_proxy")).style(switch_internal_css)
  3865. with ui.row():
  3866. input_claude2_proxies_http = ui.input(label='proxies_http', placeholder='http代理地址,默认为 http://127.0.0.1:10809', value=config.get("claude2", "proxies", "http"))
  3867. input_claude2_proxies_http.style("width:400px")
  3868. input_claude2_proxies_https = ui.input(label='proxies_https', placeholder='https代理地址,默认为 http://127.0.0.1:10809', value=config.get("claude2", "proxies", "https"))
  3869. input_claude2_proxies_https.style("width:400px")
  3870. input_claude2_proxies_socks5 = ui.input(label='proxies_socks5', placeholder='socks5代理地址,默认为 socks://127.0.0.1:10808', value=config.get("claude2", "proxies", "socks5"))
  3871. input_claude2_proxies_socks5.style("width:400px")
  3872. if config.get("webui", "show_card", "llm", "chatglm"):
  3873. with ui.card().style(card_css):
  3874. ui.label("ChatGLM1、2")
  3875. with ui.row():
  3876. input_chatglm_api_ip_port = ui.input(
  3877. label='API地址',
  3878. placeholder='ChatGLM的API版本运行后的服务链接(需要完整的URL)',
  3879. value=config.get("chatglm", "api_ip_port"),
  3880. validation={
  3881. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3882. }
  3883. )
  3884. input_chatglm_api_ip_port.style("width:400px")
  3885. input_chatglm_max_length = ui.input(label='最大长度限制', placeholder='生成回答的最大长度限制,以令牌数或字符数为单位。', value=config.get("chatglm", "max_length"))
  3886. input_chatglm_max_length.style("width:200px")
  3887. input_chatglm_top_p = ui.input(label='前p个选择', placeholder='也称为 Nucleus采样。控制模型生成时选择概率的阈值范围。', value=config.get("chatglm", "top_p"))
  3888. input_chatglm_top_p.style("width:200px")
  3889. input_chatglm_temperature = ui.input(label='温度', placeholder='温度参数,控制生成文本的随机性。较高的温度值会产生-更多的随机性和多样性。', value=config.get("chatglm", "temperature"))
  3890. input_chatglm_temperature.style("width:200px")
  3891. with ui.row():
  3892. switch_chatglm_history_enable = ui.switch('上下文记忆', value=config.get("chatglm", "history_enable")).style(switch_internal_css)
  3893. input_chatglm_history_max_len = ui.input(label='最大记忆长度', placeholder='最大记忆的上下文字符数量,不建议设置过大,容易爆显存,自行根据情况配置', value=config.get("chatglm", "history_max_len"))
  3894. input_chatglm_history_max_len.style("width:200px")
  3895. if config.get("webui", "show_card", "llm", "qwen"):
  3896. with ui.card().style(card_css):
  3897. ui.label("Qwen")
  3898. with ui.row():
  3899. input_qwen_api_ip_port = ui.input(
  3900. label='API地址',
  3901. placeholder='ChatGLM的API版本运行后的服务链接(需要完整的URL)',
  3902. value=config.get("qwen", "api_ip_port"),
  3903. validation={
  3904. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3905. }
  3906. )
  3907. input_qwen_api_ip_port.style("width:400px")
  3908. input_qwen_max_length = ui.input(label='最大长度限制', placeholder='生成回答的最大长度限制,以令牌数或字符数为单位。', value=config.get("qwen", "max_length"))
  3909. input_qwen_max_length.style("width:200px")
  3910. input_qwen_top_p = ui.input(label='前p个选择', placeholder='也称为 Nucleus采样。控制模型生成时选择概率的阈值范围。', value=config.get("qwen", "top_p"))
  3911. input_qwen_top_p.style("width:200px")
  3912. input_qwen_temperature = ui.input(label='温度', placeholder='温度参数,控制生成文本的随机性。较高的温度值会产生更多的随机性和多样性。', value=config.get("qwen", "temperature"))
  3913. input_qwen_temperature.style("width:200px")
  3914. with ui.row():
  3915. switch_qwen_history_enable = ui.switch('上下文记忆', value=config.get("qwen", "history_enable")).style(switch_internal_css)
  3916. input_qwen_history_max_len = ui.input(label='最大记忆轮数', placeholder='最大记忆的上下文轮次数量,不建议设置过大,容易爆显存,自行根据情况配置', value=config.get("qwen", "history_max_len"))
  3917. input_qwen_history_max_len.style("width:200px")
  3918. input_qwen_preset = ui.input(label='预设',
  3919. placeholder='用于指定一组预定义的设置,以便模型更好地适应特定的对话场景。',
  3920. value=config.get("chatgpt", "preset")).style("width:500px")
  3921. if config.get("webui", "show_card", "llm", "chat_with_file"):
  3922. with ui.card().style(card_css):
  3923. ui.label("chat_with_file")
  3924. with ui.row():
  3925. lines = ["claude", "openai_gpt", "openai_vector_search"]
  3926. data_json = {}
  3927. for line in lines:
  3928. data_json[line] = line
  3929. select_chat_with_file_chat_mode = ui.select(
  3930. label='聊天模式',
  3931. options=data_json,
  3932. value=config.get("chat_with_file", "chat_mode")
  3933. )
  3934. input_chat_with_file_data_path = ui.input(label='数据文件路径', placeholder='加载的本地zip数据文件路径(到x.zip), 如:./data/伊卡洛斯百度百科.zip', value=config.get("chat_with_file", "data_path"))
  3935. input_chat_with_file_data_path.style("width:400px")
  3936. with ui.row():
  3937. input_chat_with_file_separator = ui.input(label='分隔符', placeholder='拆分文本的分隔符,这里使用 换行符 作为分隔符。', value=config.get("chat_with_file", "separator"))
  3938. input_chat_with_file_separator.style("width:300px")
  3939. input_chat_with_file_chunk_size = ui.input(label='块大小', placeholder='每个文本块的最大字符数(文本块字符越多,消耗token越多,回复越详细)', value=config.get("chat_with_file", "chunk_size"))
  3940. input_chat_with_file_chunk_size.style("width:300px")
  3941. input_chat_with_file_chunk_overlap = ui.input(label='块重叠', placeholder='两个相邻文本块之间的重叠字符数。这种重叠可以帮助保持文本的连贯性,特别是当文本被用于训练语言模型或其他需要上下文信息的机器学习模型时', value=config.get("chat_with_file", "chunk_overlap"))
  3942. input_chat_with_file_chunk_overlap.style("width:300px")
  3943. lines = ["sebastian-hofstaetter/distilbert-dot-tas_b-b256-msmarco", "GanymedeNil/text2vec-large-chinese"]
  3944. data_json = {}
  3945. for line in lines:
  3946. data_json[line] = line
  3947. select_chat_with_file_local_vector_embedding_model = ui.select(
  3948. label='模型',
  3949. options=data_json,
  3950. value=config.get("chat_with_file", "local_vector_embedding_model")
  3951. )
  3952. with ui.row():
  3953. input_chat_with_file_chain_type = ui.input(label='链类型', placeholder='指定要生成的语言链的类型,例如:stuff', value=config.get("chat_with_file", "chain_type"))
  3954. input_chat_with_file_chain_type.style("width:300px")
  3955. input_chat_with_file_question_prompt = ui.input(label='问题总结提示词', placeholder='通过LLM总结本地向量数据库输出内容,此处填写总结用提示词', value=config.get("chat_with_file", "question_prompt"))
  3956. input_chat_with_file_question_prompt.style("width:300px")
  3957. input_chat_with_file_local_max_query = ui.input(label='最大查询数据库次数', placeholder='最大查询数据库次数。限制次数有助于节省token', value=config.get("chat_with_file", "local_max_query"))
  3958. input_chat_with_file_local_max_query.style("width:300px")
  3959. switch_chat_with_file_show_token_cost = ui.switch('显示成本', value=config.get("chat_with_file", "show_token_cost")).style(switch_internal_css)
  3960. if config.get("webui", "show_card", "llm", "chatterbot"):
  3961. with ui.card().style(card_css):
  3962. ui.label("Chatterbot")
  3963. with ui.grid(columns=2):
  3964. input_chatterbot_name = ui.input(label='bot名称', placeholder='bot名称', value=config.get("chatterbot", "name"))
  3965. input_chatterbot_name.style("width:400px")
  3966. input_chatterbot_db_path = ui.input(label='数据库路径', placeholder='数据库路径(绝对或相对路径)', value=config.get("chatterbot", "db_path"))
  3967. input_chatterbot_db_path.style("width:400px")
  3968. if config.get("webui", "show_card", "llm", "text_generation_webui"):
  3969. with ui.card().style(card_css):
  3970. ui.label("text_generation_webui")
  3971. with ui.row():
  3972. select_text_generation_webui_type = ui.select(
  3973. label='类型',
  3974. options={"官方API": "官方API", "coyude": "coyude"},
  3975. value=config.get("text_generation_webui", "type")
  3976. )
  3977. input_text_generation_webui_api_ip_port = ui.input(
  3978. label='API地址',
  3979. placeholder='text-generation-webui开启API模式后监听的IP和端口地址',
  3980. value=config.get("text_generation_webui", "api_ip_port"),
  3981. validation={
  3982. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  3983. }
  3984. )
  3985. input_text_generation_webui_api_ip_port.style("width:300px")
  3986. input_text_generation_webui_max_new_tokens = ui.input(label='max_new_tokens', placeholder='自行查阅', value=config.get("text_generation_webui", "max_new_tokens"))
  3987. input_text_generation_webui_max_new_tokens.style("width:200px")
  3988. switch_text_generation_webui_history_enable = ui.switch('上下文记忆', value=config.get("text_generation_webui", "history_enable")).style(switch_internal_css)
  3989. input_text_generation_webui_history_max_len = ui.input(label='最大记忆长度', placeholder='最大记忆的上下文字符数量,不建议设置过大,容易爆显存,自行根据情况配置', value=config.get("text_generation_webui", "history_max_len"))
  3990. input_text_generation_webui_history_max_len.style("width:200px")
  3991. with ui.row():
  3992. select_text_generation_webui_mode = ui.select(
  3993. label='类型',
  3994. options={"chat": "chat", "chat-instruct": "chat-instruct", "instruct": "instruct"},
  3995. value=config.get("text_generation_webui", "mode")
  3996. ).style("width:150px")
  3997. input_text_generation_webui_character = ui.input(label='character', placeholder='自行查阅', value=config.get("text_generation_webui", "character"))
  3998. input_text_generation_webui_character.style("width:100px")
  3999. input_text_generation_webui_instruction_template = ui.input(label='instruction_template', placeholder='自行查阅', value=config.get("text_generation_webui", "instruction_template"))
  4000. input_text_generation_webui_instruction_template.style("width:150px")
  4001. input_text_generation_webui_your_name = ui.input(label='your_name', placeholder='自行查阅', value=config.get("text_generation_webui", "your_name"))
  4002. input_text_generation_webui_your_name.style("width:100px")
  4003. with ui.row():
  4004. input_text_generation_webui_top_p = ui.input(label='top_p', value=config.get("text_generation_webui", "top_p"), placeholder='topP生成时,核采样方法的概率阈值。例如,取值为0.8时,仅保留累计概率之和大于等于0.8的概率分布中的token,作为随机采样的候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的随机性越低。默认值 0.95。注意,取值不要大于等于1')
  4005. input_text_generation_webui_top_k = ui.input(label='top_k', value=config.get("text_generation_webui", "top_k"), placeholder='匹配搜索结果条数')
  4006. input_text_generation_webui_temperature = ui.input(label='temperature', value=config.get("text_generation_webui", "temperature"), placeholder='较高的值将使输出更加随机,而较低的值将使输出更加集中和确定。可选,默认取值0.92')
  4007. input_text_generation_webui_seed = ui.input(label='seed', value=config.get("text_generation_webui", "seed"), placeholder='seed生成时,随机数的种子,用于控制模型生成的随机性。如果使用相同的种子,每次运行生成的结果都将相同;当需要复现模型的生成结果时,可以使用相同的种子。seed参数支持无符号64位整数类型。默认值 1683806810')
  4008. if config.get("webui", "show_card", "llm", "sparkdesk"):
  4009. with ui.card().style(card_css):
  4010. ui.label("讯飞星火")
  4011. with ui.grid(columns=1):
  4012. lines = ["web", "api"]
  4013. data_json = {}
  4014. for line in lines:
  4015. data_json[line] = line
  4016. select_sparkdesk_type = ui.select(
  4017. label='类型',
  4018. options=data_json,
  4019. value=config.get("sparkdesk", "type")
  4020. ).style("width:100px")
  4021. with ui.card().style(card_css):
  4022. ui.label("WEB")
  4023. with ui.row():
  4024. input_sparkdesk_cookie = ui.input(label='cookie', placeholder='web抓包请求头中的cookie,参考文档教程', value=config.get("sparkdesk", "cookie"))
  4025. input_sparkdesk_cookie.style("width:300px")
  4026. input_sparkdesk_fd = ui.input(label='fd', placeholder='web抓包负载中的fd,参考文档教程', value=config.get("sparkdesk", "fd"))
  4027. input_sparkdesk_fd.style("width:200px")
  4028. input_sparkdesk_GtToken = ui.input(label='GtToken', placeholder='web抓包负载中的GtToken,参考文档教程', value=config.get("sparkdesk", "GtToken"))
  4029. input_sparkdesk_GtToken.style("width:200px")
  4030. with ui.card().style(card_css):
  4031. ui.label("API")
  4032. with ui.row():
  4033. input_sparkdesk_app_id = ui.input(label='app_id', value=config.get("sparkdesk", "app_id"), placeholder='申请官方API后,云平台中提供的APPID').style("width:100px")
  4034. input_sparkdesk_api_secret = ui.input(label='api_secret', value=config.get("sparkdesk", "api_secret"), placeholder='申请官方API后,云平台中提供的APISecret').style("width:200px")
  4035. input_sparkdesk_api_key = ui.input(label='api_key', value=config.get("sparkdesk", "api_key"), placeholder='申请官方API后,云平台中提供的APIKey').style("width:200px")
  4036. select_sparkdesk_version = ui.select(
  4037. label='版本',
  4038. options={
  4039. "4.0": "Ultra",
  4040. "3.5": "Max",
  4041. "3.2": "pro-128k",
  4042. "3.1": "Pro",
  4043. "2.1": "V2.1",
  4044. "1.1": "Lite",
  4045. },
  4046. value=str(config.get("sparkdesk", "version"))
  4047. ).style("width:100px")
  4048. input_sparkdesk_assistant_id = ui.input(label='助手ID', value=config.get("sparkdesk", "assistant_id"), placeholder='助手创作中心,创建助手后助手API的接口地址最后的助手ID').style("width:100px")
  4049. if config.get("webui", "show_card", "llm", "langchain_chatglm"):
  4050. with ui.card().style(card_css):
  4051. ui.label("Langchain_ChatGLM")
  4052. with ui.row():
  4053. input_langchain_chatglm_api_ip_port = ui.input(
  4054. label='API地址',
  4055. placeholder='langchain_chatglm的API版本运行后的服务链接(需要完整的URL)',
  4056. value=config.get("langchain_chatglm", "api_ip_port"),
  4057. validation={
  4058. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4059. }
  4060. )
  4061. input_langchain_chatglm_api_ip_port.style("width:400px")
  4062. lines = ["模型", "知识库", "必应"]
  4063. data_json = {}
  4064. for line in lines:
  4065. data_json[line] = line
  4066. select_langchain_chatglm_chat_type = ui.select(
  4067. label='类型',
  4068. options=data_json,
  4069. value=config.get("langchain_chatglm", "chat_type")
  4070. )
  4071. with ui.row():
  4072. input_langchain_chatglm_knowledge_base_id = ui.input(label='知识库名称', placeholder='本地存在的知识库名称,日志也有输出知识库列表,可以查看', value=config.get("langchain_chatglm", "knowledge_base_id"))
  4073. input_langchain_chatglm_knowledge_base_id.style("width:400px")
  4074. switch_langchain_chatglm_history_enable = ui.switch('上下文记忆', value=config.get("langchain_chatglm", "history_enable")).style(switch_internal_css)
  4075. input_langchain_chatglm_history_max_len = ui.input(label='最大记忆长度', placeholder='最大记忆的上下文字符数量,不建议设置过大,容易爆显存,自行根据情况配置', value=config.get("langchain_chatglm", "history_max_len"))
  4076. input_langchain_chatglm_history_max_len.style("width:400px")
  4077. if config.get("webui", "show_card", "llm", "langchain_chatchat"):
  4078. with ui.card().style(card_css):
  4079. ui.label("Langchain_ChatChat")
  4080. with ui.row():
  4081. input_langchain_chatchat_api_ip_port = ui.input(
  4082. label='API地址',
  4083. placeholder='langchain_chatchat的API版本运行后的服务链接(需要完整的URL)',
  4084. value=config.get("langchain_chatchat", "api_ip_port"),
  4085. validation={
  4086. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4087. }
  4088. )
  4089. input_langchain_chatchat_api_ip_port.style("width:400px")
  4090. lines = ["模型", "知识库", "搜索引擎"]
  4091. data_json = {}
  4092. for line in lines:
  4093. data_json[line] = line
  4094. select_langchain_chatchat_chat_type = ui.select(
  4095. label='类型',
  4096. options=data_json,
  4097. value=config.get("langchain_chatchat", "chat_type")
  4098. )
  4099. switch_langchain_chatchat_history_enable = ui.switch('上下文记忆', value=config.get("langchain_chatchat", "history_enable")).style(switch_internal_css)
  4100. input_langchain_chatchat_history_max_len = ui.input(label='最大记忆长度', placeholder='最大记忆的上下文字符数量,不建议设置过大,容易爆显存,自行根据情况配置', value=config.get("langchain_chatchat", "history_max_len"))
  4101. input_langchain_chatchat_history_max_len.style("width:400px")
  4102. with ui.row():
  4103. with ui.card().style(card_css):
  4104. ui.label("模型")
  4105. with ui.row():
  4106. input_langchain_chatchat_llm_model_name = ui.input(label='LLM模型', value=config.get("langchain_chatchat", "llm", "model_name"), placeholder='本地加载的LLM模型名')
  4107. input_langchain_chatchat_llm_temperature = ui.input(label='温度', value=config.get("langchain_chatchat", "llm", "temperature"), placeholder='采样温度,控制输出的随机性,必须为正数\n取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95\n值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定\n建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数')
  4108. input_langchain_chatchat_llm_max_tokens = ui.input(label='max_tokens', value=config.get("langchain_chatchat", "llm", "max_tokens"), placeholder='大于0的正整数,不建议太大,你可能会爆显存')
  4109. input_langchain_chatchat_llm_prompt_name = ui.input(label='Prompt模板', value=config.get("langchain_chatchat", "llm", "prompt_name"), placeholder='本地存在的提示词模板文件名')
  4110. with ui.row():
  4111. with ui.card().style(card_css):
  4112. ui.label("知识库")
  4113. with ui.row():
  4114. input_langchain_chatchat_knowledge_base_knowledge_base_name = ui.input(label='知识库名', value=config.get("langchain_chatchat", "knowledge_base", "knowledge_base_name"), placeholder='本地添加的知识库名,运行时会自动检索存在的知识库列表,输出到cmd,请自行查看')
  4115. input_langchain_chatchat_knowledge_base_top_k = ui.input(label='匹配搜索结果条数', value=config.get("langchain_chatchat", "knowledge_base", "top_k"), placeholder='匹配搜索结果条数')
  4116. input_langchain_chatchat_knowledge_base_score_threshold = ui.input(label='知识匹配分数阈值', value=config.get("langchain_chatchat", "knowledge_base", "score_threshold"), placeholder='0.00-2.00之间')
  4117. input_langchain_chatchat_knowledge_base_model_name = ui.input(label='LLM模型', value=config.get("langchain_chatchat", "knowledge_base", "model_name"), placeholder='本地加载的LLM模型名')
  4118. input_langchain_chatchat_knowledge_base_temperature = ui.input(label='温度', value=config.get("langchain_chatchat", "knowledge_base", "temperature"), placeholder='采样温度,控制输出的随机性,必须为正数\n取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95\n值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定\n建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数')
  4119. input_langchain_chatchat_knowledge_base_max_tokens = ui.input(label='max_tokens', value=config.get("langchain_chatchat", "knowledge_base", "max_tokens"), placeholder='大于0的正整数,不建议太大,你可能会爆显存')
  4120. input_langchain_chatchat_knowledge_base_prompt_name = ui.input(label='Prompt模板', value=config.get("langchain_chatchat", "knowledge_base", "prompt_name"), placeholder='本地存在的提示词模板文件名')
  4121. with ui.row():
  4122. with ui.card().style(card_css):
  4123. ui.label("搜索引擎")
  4124. with ui.row():
  4125. lines = ['bing', 'duckduckgo', 'metaphor']
  4126. data_json = {}
  4127. for line in lines:
  4128. data_json[line] = line
  4129. select_langchain_chatchat_search_engine_search_engine_name = ui.select(
  4130. label='搜索引擎',
  4131. options=data_json,
  4132. value=config.get("langchain_chatchat", "search_engine", "search_engine_name")
  4133. )
  4134. input_langchain_chatchat_search_engine_top_k = ui.input(label='匹配搜索结果条数', value=config.get("langchain_chatchat", "search_engine", "top_k"), placeholder='匹配搜索结果条数')
  4135. input_langchain_chatchat_search_engine_model_name = ui.input(label='LLM模型', value=config.get("langchain_chatchat", "search_engine", "model_name"), placeholder='本地加载的LLM模型名')
  4136. input_langchain_chatchat_search_engine_temperature = ui.input(label='温度', value=config.get("langchain_chatchat", "search_engine", "temperature"), placeholder='采样温度,控制输出的随机性,必须为正数\n取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95\n值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定\n建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数')
  4137. input_langchain_chatchat_search_engine_max_tokens = ui.input(label='max_tokens', value=config.get("langchain_chatchat", "search_engine", "max_tokens"), placeholder='大于0的正整数,不建议太大,你可能会爆显存')
  4138. input_langchain_chatchat_search_engine_prompt_name = ui.input(label='Prompt模板', value=config.get("langchain_chatchat", "search_engine", "prompt_name"), placeholder='本地存在的提示词模板文件名')
  4139. if config.get("webui", "show_card", "llm", "zhipu"):
  4140. with ui.card().style(card_css):
  4141. ui.label("智谱AI")
  4142. with ui.row():
  4143. input_zhipu_api_key = ui.input(label='api key', placeholder='具体参考官方文档,申请地址:https://open.bigmodel.cn/usercenter/apikeys', value=config.get("zhipu", "api_key"))
  4144. input_zhipu_api_key.style("width:200px")
  4145. lines = [
  4146. '应用',
  4147. '智能体',
  4148. 'glm-3-turbo',
  4149. 'glm-4',
  4150. 'glm-4-flash',
  4151. 'charglm-3',
  4152. 'characterglm',
  4153. 'chatglm_turbo',
  4154. 'chatglm_pro',
  4155. 'chatglm_std',
  4156. 'chatglm_lite',
  4157. 'chatglm_lite_32k'
  4158. ]
  4159. data_json = {}
  4160. for line in lines:
  4161. data_json[line] = line
  4162. select_zhipu_model = ui.select(
  4163. label='模型',
  4164. options=data_json,
  4165. value=config.get("zhipu", "model"),
  4166. with_input=True,
  4167. new_value_mode='add-unique',
  4168. clearable=True
  4169. )
  4170. input_zhipu_app_id = ui.input(label='应用ID', value=config.get("zhipu", "app_id"), placeholder='在 模型为:应用,会自动检索你平台上添加的所有应用信息,然后从日志中复制你需要的应用ID即可').style("width:200px")
  4171. with ui.row():
  4172. input_zhipu_top_p = ui.input(label='top_p', placeholder='用温度取样的另一种方法,称为核取样\n取值范围是:(0.0,1.0);开区间,不能等于 0 或 1,默认值为 0.7\n模型考虑具有 top_p 概率质量的令牌的结果。所以 0.1 意味着模型解码器只考虑从前 10% 的概率的候选集中取tokens\n建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数', value=config.get("zhipu", "top_p"))
  4173. input_zhipu_top_p.style("width:200px")
  4174. input_zhipu_temperature = ui.input(label='temperature', placeholder='采样温度,控制输出的随机性,必须为正数\n取值范围是:(0.0,1.0],不能等于 0,默认值为 0.95\n值越大,会使输出更随机,更具创造性;值越小,输出会更加稳定或确定\n建议您根据应用场景调整 top_p 或 temperature 参数,但不要同时调整两个参数', value=config.get("zhipu", "temperature"))
  4175. input_zhipu_temperature.style("width:200px")
  4176. switch_zhipu_history_enable = ui.switch('上下文记忆', value=config.get("zhipu", "history_enable")).style(switch_internal_css)
  4177. input_zhipu_history_max_len = ui.input(label='最大记忆长度', placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米', value=config.get("zhipu", "history_max_len"))
  4178. input_zhipu_history_max_len.style("width:200px")
  4179. with ui.row():
  4180. input_zhipu_user_info = ui.input(label='用户信息', placeholder='用户信息,当使用characterglm时需要配置', value=config.get("zhipu", "user_info"))
  4181. input_zhipu_user_info.style("width:400px")
  4182. input_zhipu_bot_info = ui.input(label='角色信息', placeholder='角色信息,当使用characterglm时需要配置', value=config.get("zhipu", "bot_info"))
  4183. input_zhipu_bot_info.style("width:400px")
  4184. input_zhipu_bot_name = ui.input(label='角色名称', placeholder='角色名称,当使用characterglm时需要配置', value=config.get("zhipu", "bot_name"))
  4185. input_zhipu_bot_name.style("width:200px")
  4186. input_zhipu_username = ui.input(label='用户名称', placeholder='用户名称,默认值为用户,当使用characterglm时需要配置', value=config.get("zhipu", "username"))
  4187. input_zhipu_username.style("width:200px")
  4188. with ui.row():
  4189. switch_zhipu_remove_useless = ui.switch('删除无用字符', value=config.get("zhipu", "remove_useless")).style(switch_internal_css)
  4190. switch_zhipu_stream = ui.switch('流式输出', value=config.get("zhipu", "stream")).tooltip("是否开启流式输出,开启后,回答会逐句输出,关闭后,回答会一次性输出。")
  4191. with ui.card().style(card_css):
  4192. ui.label("智能体")
  4193. with ui.row():
  4194. input_zhipu_assistant_api_api_key = ui.input(
  4195. label='智能体API Key',
  4196. placeholder='智能体 创作者中心申请API:https://chatglm.cn/developersPanel/apiSet',
  4197. value=config.get("zhipu", "assistant_api", "api_key")
  4198. ).style("width:150px").tooltip('智能体 创作者中心申请API:https://chatglm.cn/developersPanel/apiSet')
  4199. input_zhipu_assistant_api_api_secret = ui.input(
  4200. label='智能体API Secret',
  4201. placeholder='智能体 创作者中心申请API:https://chatglm.cn/developersPanel/apiSet',
  4202. value=config.get("zhipu", "assistant_api", "api_secret")
  4203. ).style("width:150px").tooltip('智能体 创作者中心申请API:https://chatglm.cn/developersPanel/apiSet')
  4204. input_zhipu_assistant_api_assistant_id = ui.input(
  4205. label='智能体ID',
  4206. placeholder='智能体 ID,浏览器打开智能体对话页后,可通过URL地址栏查看,那一串英文数字',
  4207. value=config.get("zhipu", "assistant_api", "assistant_id")
  4208. ).style("width:200px").tooltip('智能体 ID,浏览器打开智能体对话页后,可通过URL地址栏查看,那一串英文数字')
  4209. if config.get("webui", "show_card", "llm", "bard"):
  4210. with ui.card().style(card_css):
  4211. ui.label("Bard")
  4212. with ui.grid(columns=2):
  4213. input_bard_token = ui.input(label='token', placeholder='登录bard,打开F12,在cookie中获取 __Secure-1PSID 对应的值', value=config.get("bard", "token"))
  4214. input_bard_token.style("width:400px")
  4215. if config.get("webui", "show_card", "llm", "tongyixingchen"):
  4216. with ui.card().style(card_css):
  4217. ui.label("通义星尘")
  4218. with ui.row():
  4219. input_tongyixingchen_access_token = ui.input(label='密钥', value=config.get("tongyixingchen", "access_token"), placeholder='官网申请开通API-KEY,然后找官方申请调用权限')
  4220. lines = ['固定角色']
  4221. data_json = {}
  4222. for line in lines:
  4223. data_json[line] = line
  4224. select_tongyixingchen_type = ui.select(
  4225. label='类型',
  4226. options=data_json,
  4227. value=config.get("tongyixingchen", "type")
  4228. ).style("width:100px")
  4229. switch_tongyixingchen_history_enable = ui.switch('上下文记忆', value=config.get("tongyixingchen", "history_enable")).style(switch_internal_css)
  4230. input_tongyixingchen_history_max_len = ui.input(label='最大记忆长度', value=config.get("tongyixingchen", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4231. switch_tongyixingchen_stream = ui.switch('流式输出', value=config.get("tongyixingchen", "stream")).tooltip("是否开启流式输出,开启后,回答会逐句输出,关闭后,回答会一次性输出。")
  4232. with ui.card().style(card_css):
  4233. ui.label("固定角色")
  4234. with ui.row():
  4235. input_tongyixingchen_GDJS_character_id = ui.input(label='角色ID', value=config.get("tongyixingchen", "固定角色", "character_id"), placeholder='官网聊天页,创建的角色,然后点开角色的信息,可以看见ID')
  4236. input_tongyixingchen_GDJS_top_p = ui.input(label='top_p', value=config.get("tongyixingchen", "固定角色", "top_p"), placeholder='topP生成时,核采样方法的概率阈值。例如,取值为0.8时,仅保留累计概率之和大于等于0.8的概率分布中的token,作为随机采样的候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的随机性越低。默认值 0.95。注意,取值不要大于等于1')
  4237. input_tongyixingchen_GDJS_temperature = ui.input(label='temperature', value=config.get("tongyixingchen", "固定角色", "temperature"), placeholder='较高的值将使输出更加随机,而较低的值将使输出更加集中和确定。可选,默认取值0.92')
  4238. input_tongyixingchen_GDJS_seed = ui.input(label='seed', value=config.get("tongyixingchen", "固定角色", "seed"), placeholder='seed生成时,随机数的种子,用于控制模型生成的随机性。如果使用相同的种子,每次运行生成的结果都将相同;当需要复现模型的生成结果时,可以使用相同的种子。seed参数支持无符号64位整数类型。默认值 1683806810')
  4239. with ui.row():
  4240. input_tongyixingchen_GDJS_user_id = ui.input(label='用户ID', value=config.get("tongyixingchen", "固定角色", "user_id"), placeholder='业务系统用户唯一标识,同一用户不能并行对话,必须待上次对话回复结束后才可发起下轮对话')
  4241. input_tongyixingchen_GDJS_username = ui.input(label='对话用户名称', value=config.get("tongyixingchen", "固定角色", "username"), placeholder='对话用户名称,即你的名字')
  4242. input_tongyixingchen_GDJS_role_name = ui.input(label='固定角色名称', value=config.get("tongyixingchen", "固定角色", "role_name"), placeholder='角色ID对应的角色名称,自己编写的别告诉我你不知道!')
  4243. if config.get("webui", "show_card", "llm", "my_wenxinworkshop"):
  4244. with ui.card().style(card_css):
  4245. ui.label("千帆大模型")
  4246. with ui.row():
  4247. select_my_wenxinworkshop_type = ui.select(
  4248. label='类型',
  4249. options={"千帆大模型": "千帆大模型", "AppBuilder": "AppBuilder"},
  4250. value=config.get("my_wenxinworkshop", "type")
  4251. ).style("width:150px")
  4252. switch_my_wenxinworkshop_history_enable = ui.switch('上下文记忆', value=config.get("my_wenxinworkshop", "history_enable")).style(switch_internal_css)
  4253. input_my_wenxinworkshop_history_max_len = ui.input(label='最大记忆长度', value=config.get("my_wenxinworkshop", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4254. switch_my_wenxinworkshop_stream = ui.switch('流式输出', value=config.get("my_wenxinworkshop", "stream")).tooltip("是否开启流式输出,开启后,回答会逐句输出,关闭后,回答会一次性输出。")
  4255. with ui.row():
  4256. input_my_wenxinworkshop_api_key = ui.input(label='api_key', value=config.get("my_wenxinworkshop", "api_key"), placeholder='千帆大模型平台,开通对应服务。应用接入-创建应用,填入api key')
  4257. input_my_wenxinworkshop_secret_key = ui.input(label='secret_key', value=config.get("my_wenxinworkshop", "secret_key"), placeholder='千帆大模型平台,开通对应服务。应用接入-创建应用,填入secret key')
  4258. lines = [
  4259. "ERNIEBot",
  4260. "ERNIEBot_turbo",
  4261. "ERNIEBot_4_0",
  4262. "ERNIE_SPEED_128K",
  4263. "ERNIE_SPEED_8K",
  4264. "ERNIE_LITE_8K",
  4265. "ERNIE_LITE_8K_0922",
  4266. "ERNIE_TINY_8K",
  4267. "BLOOMZ_7B",
  4268. "LLAMA_2_7B",
  4269. "LLAMA_2_13B",
  4270. "LLAMA_2_70B",
  4271. "ERNIEBot_4_0",
  4272. "QIANFAN_BLOOMZ_7B_COMPRESSED",
  4273. "QIANFAN_CHINESE_LLAMA_2_7B",
  4274. "CHATGLM2_6B_32K",
  4275. "AQUILACHAT_7B",
  4276. "ERNIE_BOT_8K",
  4277. "CODELLAMA_7B_INSTRUCT",
  4278. "XUANYUAN_70B_CHAT",
  4279. "CHATLAW",
  4280. "QIANFAN_BLOOMZ_7B_COMPRESSED",
  4281. ]
  4282. data_json = {}
  4283. for line in lines:
  4284. data_json[line] = line
  4285. select_my_wenxinworkshop_model = ui.select(
  4286. label='模型',
  4287. options=data_json,
  4288. value=config.get("my_wenxinworkshop", "model")
  4289. ).style("width:150px")
  4290. input_my_wenxinworkshop_temperature = ui.input(label='温度', value=config.get("my_wenxinworkshop", "temperature"), placeholder='(0, 1.0] 控制生成文本的随机性。较高的温度值会使生成的文本更随机和多样化,而较低的温度值会使生成的文本更加确定和一致。').style("width:200px;")
  4291. input_my_wenxinworkshop_top_p = ui.input(label='前p个选择', value=config.get("my_wenxinworkshop", "top_p"), placeholder='[0, 1.0] Nucleus采样。这个参数控制模型从累积概率大于一定阈值的令牌中进行采样。较高的值会产生更多的多样性,较低的值会产生更少但更确定的回答。').style("width:200px;")
  4292. input_my_wenxinworkshop_penalty_score = ui.input(label='惩罚得分', value=config.get("my_wenxinworkshop", "penalty_score"), placeholder='[1.0, 2.0] 在生成文本时对某些词语或模式施加的惩罚。这是一种调节生成内容的机制,用来减少或避免不希望出现的内容。').style("width:200px;")
  4293. with ui.row():
  4294. input_my_wenxinworkshop_app_id = ui.input(label='AppBuilder 应用ID', value=config.get("my_wenxinworkshop", "app_id"), placeholder='千帆AppBuilder平台,个人空间 应用 应用ID').style("width:200px;")
  4295. input_my_wenxinworkshop_app_token = ui.input(label='AppBuilder app_token', value=config.get("my_wenxinworkshop", "app_token"), placeholder='千帆AppBuilder平台,我的应用-应用配置-发布详情-我的Agent应用-API调用,填入app_token').style("width:200px;")
  4296. # with ui.card().style(card_css):
  4297. # ui.label("千帆大模型(兼容问题暂不启用)")
  4298. # with ui.row():
  4299. # input_my_qianfan_access_key = ui.input(label='access_key', value=config.get("my_qianfan", "access_key"), placeholder='官网右上角安全认证申请开通access_key')
  4300. # input_my_qianfan_secret_key = ui.input(label='secret_key', value=config.get("my_qianfan", "secret_key"), placeholder='官网右上角安全认证申请开通access_key')
  4301. # lines = [
  4302. # 'ERNIE-Bot-turbo',
  4303. # 'ERNIE-Bot',
  4304. # 'ERNIE-Bot-4',
  4305. # 'BLOOMZ-7B',
  4306. # 'Llama-2-7b-chat',
  4307. # 'Llama-2-13b-chat',
  4308. # 'Llama-2-70b-chat',
  4309. # 'Qianfan-BLOOMZ-7B-compressed',
  4310. # 'Qianfan-Chinese-Llama-2-7B',
  4311. # 'ChatGLM2-6B-32K',
  4312. # 'AquilaChat-7B'
  4313. # ]
  4314. # data_json = {}
  4315. # for line in lines:
  4316. # data_json[line] = line
  4317. # select_my_qianfan_model = ui.select(
  4318. # label='模型',
  4319. # options=data_json,
  4320. # value=config.get("my_qianfan", "model")
  4321. # ).style("width:150px")
  4322. # switch_my_qianfan_history_enable = ui.switch('上下文记忆', value=config.get("my_qianfan", "history_enable")).style(switch_internal_css)
  4323. # input_my_qianfan_history_max_len = ui.input(label='最大记忆长度', value=config.get("my_qianfan", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4324. # with ui.row():
  4325. # input_my_qianfan_temperature = ui.input(label='温度', value=config.get("my_qianfan", "temperature"), placeholder='控制生成文本的随机性。较高的温度值会使生成的文本更随机和多样化,而较低的温度值会使生成的文本更加确定和一致。').style("width:200px;")
  4326. # input_my_qianfan_top_p = ui.input(label='前p个选择', value=config.get("my_qianfan", "top_p"), placeholder='Nucleus采样。这个参数控制模型从累积概率大于一定阈值的令牌中进行采样。较高的值会产生更多的多样性,较低的值会产生更少但更确定的回答。').style("width:200px;")
  4327. # input_my_qianfan_penalty_score = ui.input(label='惩罚得分', value=config.get("my_qianfan", "penalty_score"), placeholder='在生成文本时对某些词语或模式施加的惩罚。这是一种调节生成内容的机制,用来减少或避免不希望出现的内容。').style("width:200px;")
  4328. if config.get("webui", "show_card", "llm", "gemini"):
  4329. with ui.card().style(card_css):
  4330. ui.label("Gemini")
  4331. with ui.row():
  4332. input_gemini_api_key = ui.input(label='api_key', value=config.get("gemini", "api_key"), placeholder='谷歌AI Studio创建api key')
  4333. lines = [
  4334. "gemini-pro",
  4335. ]
  4336. data_json = {}
  4337. for line in lines:
  4338. data_json[line] = line
  4339. select_gemini_model = ui.select(
  4340. label='模型',
  4341. options=data_json,
  4342. value=config.get("gemini", "model")
  4343. ).style("width:150px")
  4344. switch_gemini_history_enable = ui.switch('上下文记忆', value=config.get("gemini", "history_enable")).style(switch_internal_css)
  4345. input_gemini_history_max_len = ui.input(label='最大记忆长度', value=config.get("gemini", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4346. with ui.row():
  4347. input_gemini_http_proxy = ui.input(label='HTTP代理地址', value=config.get("gemini", "http_proxy"), placeholder='http代理地址,需要魔法才能使用,所以需要配置此项。').style("width:200px;")
  4348. input_gemini_https_proxy = ui.input(label='HTTPS代理地址', value=config.get("gemini", "https_proxy"), placeholder='https代理地址,需要魔法才能使用,所以需要配置此项。').style("width:200px;")
  4349. with ui.row():
  4350. input_gemini_max_output_tokens = ui.input(label='最大输出token数', value=config.get("gemini", "max_output_tokens"), placeholder='候选输出中包含的最大token数')
  4351. input_gemini_max_temperature = ui.input(label='temperature', value=config.get("gemini", "temperature"), placeholder='控制输出的随机性。值范围为[0.0,1.0],包括0.0和1.0。值越接近1.0,生成的响应将更加多样化和创造性,而值越接近0.0,通常会导致模型产生更加直接的响应。')
  4352. input_gemini_top_p = ui.input(label='top_p', value=config.get("gemini", "top_p"), placeholder='在抽样时考虑的标记的最大累积概率。根据其分配的概率对标记进行排序,以仅考虑最可能的标记。Top-k采样直接限制要考虑的标记的最大数量,而Nucleus采样则基于累积概率限制标记的数量。')
  4353. input_gemini_top_k = ui.input(label='top_k', value=config.get("gemini", "top_k"), placeholder='在抽样时考虑的标记的最大数量。Top-k采样考虑一组top_k最有可能的标记。默认值为40。')
  4354. if config.get("webui", "show_card", "llm", "qanything"):
  4355. with ui.card().style(card_css):
  4356. ui.label("QAnything")
  4357. with ui.row():
  4358. select_qanything_type = ui.select(
  4359. label='类型',
  4360. options={'online': '在线API', 'local': '本地API'},
  4361. value=config.get("qanything", "type")
  4362. ).style("width:200px")
  4363. input_qanything_app_key = ui.input(label='应用ID', value=config.get("qanything", "app_key"), placeholder='在线平台 应用ID')
  4364. input_qanything_app_secret = ui.input(label='密钥', value=config.get("qanything", "app_secret"), placeholder='在线平台 密钥')
  4365. input_qanything_api_ip_port = ui.input(
  4366. label='API地址',
  4367. value=config.get("qanything", "api_ip_port"),
  4368. placeholder='qanything启动后API监听的ip端口地址',
  4369. validation={
  4370. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4371. }
  4372. )
  4373. with ui.row():
  4374. input_qanything_user_id = ui.input(label='用户ID', value=config.get("qanything", "user_id"), placeholder='用户ID,默认的就是 zzp')
  4375. textarea_qanything_kb_ids = ui.textarea(label='知识库ID', placeholder='知识库ID,启动时会自动检索输出日志', value=textarea_data_change(config.get("qanything", "kb_ids"))).style("width:300px;")
  4376. switch_qanything_history_enable = ui.switch('上下文记忆', value=config.get("qanything", "history_enable")).style(switch_internal_css)
  4377. input_qanything_history_max_len = ui.input(label='最大记忆长度', value=config.get("qanything", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4378. if config.get("webui", "show_card", "llm", "koboldcpp"):
  4379. with ui.card().style(card_css):
  4380. ui.label("koboldcpp")
  4381. with ui.row():
  4382. input_koboldcpp_api_ip_port = ui.input(
  4383. label='API地址',
  4384. value=config.get("koboldcpp", "api_ip_port"),
  4385. placeholder='koboldcpp启动后API监听的ip端口地址',
  4386. validation={
  4387. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4388. }
  4389. )
  4390. input_koboldcpp_max_context_length = ui.input(label='max_context_length', value=config.get("koboldcpp", "max_context_length"), placeholder='max_context_length')
  4391. input_koboldcpp_max_length = ui.input(label='max_length', value=config.get("koboldcpp", "max_length"), placeholder='max_length')
  4392. switch_koboldcpp_quiet = ui.switch('quiet', value=config.get("koboldcpp", "quiet")).style(switch_internal_css)
  4393. input_koboldcpp_rep_pen = ui.input(label='rep_pen', value=config.get("koboldcpp", "rep_pen"), placeholder='rep_pen')
  4394. input_koboldcpp_rep_pen_range = ui.input(label='rep_pen_range', value=config.get("koboldcpp", "rep_pen_range"), placeholder='rep_pen_range')
  4395. input_koboldcpp_rep_pen_slope = ui.input(label='rep_pen_slope', value=config.get("koboldcpp", "rep_pen_slope"), placeholder='rep_pen_slope')
  4396. with ui.row():
  4397. input_koboldcpp_temperature = ui.input(label='temperature', value=config.get("koboldcpp", "temperature"), placeholder='控制输出的随机性。')
  4398. input_koboldcpp_tfs = ui.input(label='tfs', value=config.get("koboldcpp", "tfs"), placeholder='tfs')
  4399. input_koboldcpp_top_a = ui.input(label='top_a', value=config.get("koboldcpp", "top_a"), placeholder='top_a')
  4400. input_koboldcpp_top_p = ui.input(label='top_p', value=config.get("koboldcpp", "top_p"), placeholder='在抽样时考虑的标记的最大累积概率。根据其分配的概率对标记进行排序,以仅考虑最可能的标记。Top-k采样直接限制要考虑的标记的最大数量,而Nucleus采样则基于累积概率限制标记的数量。')
  4401. input_koboldcpp_top_k = ui.input(label='top_k', value=config.get("koboldcpp", "top_k"), placeholder='在抽样时考虑的标记的最大数量。Top-k采样考虑一组top_k最有可能的标记。默认值为40。')
  4402. input_koboldcpp_typical = ui.input(label='typical', value=config.get("koboldcpp", "typical"), placeholder='typical')
  4403. switch_koboldcpp_history_enable = ui.switch('上下文记忆', value=config.get("koboldcpp", "history_enable")).style(switch_internal_css)
  4404. input_koboldcpp_history_max_len = ui.input(label='最大记忆长度', value=config.get("koboldcpp", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4405. if config.get("webui", "show_card", "llm", "anythingllm"):
  4406. with ui.card().style(card_css):
  4407. ui.label("AnythingLLM")
  4408. with ui.row():
  4409. input_anythingllm_api_ip_port = ui.input(
  4410. label='API地址',
  4411. value=config.get("anythingllm", "api_ip_port"),
  4412. placeholder='anythingllm启动后API监听的ip端口地址',
  4413. validation={
  4414. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4415. }
  4416. )
  4417. input_anythingllm_api_key = ui.input(label='API密钥', value=config.get("anythingllm", "api_key"), placeholder='API密钥,设置里面获取')
  4418. select_anythingllm_mode = ui.select(
  4419. label='模式',
  4420. options={'chat': '聊天', 'query': '仅查询知识库'},
  4421. value=config.get("anythingllm", "mode")
  4422. ).style("width:200px")
  4423. select_anythingllm_workspace_slug = ui.select(
  4424. label='工作区slug',
  4425. options={config.get("anythingllm", "workspace_slug"): config.get("anythingllm", "workspace_slug")},
  4426. value=config.get("anythingllm", "workspace_slug")
  4427. ).style("width:200px")
  4428. def anythingllm_get_workspaces_list():
  4429. try:
  4430. from utils.gpt_model.anythingllm import AnythingLLM
  4431. tmp_config = config.get("anythingllm")
  4432. tmp_config["api_ip_port"] = input_anythingllm_api_ip_port.value
  4433. tmp_config["api_key"] = input_anythingllm_api_key.value
  4434. anythingllm = AnythingLLM(tmp_config)
  4435. workspaces_list = anythingllm.get_workspaces_list()
  4436. data_json = {}
  4437. for workspace_info in workspaces_list:
  4438. data_json[workspace_info['slug']] = workspace_info['slug']
  4439. select_anythingllm_workspace_slug.set_options(data_json)
  4440. select_anythingllm_workspace_slug.set_value(config.get("anythingllm", "workspace_slug"))
  4441. logger.info("读取工作区成功")
  4442. ui.notify(position="top", type="positive", message="读取工作区成功")
  4443. except Exception as e:
  4444. logger.error(f"读取工作区失败!\n{e}")
  4445. ui.notify(position="top", type="negative", message=f"读取工作区失败!\n{e}")
  4446. button_anythingllm_get_workspaces_list = ui.button('获取所有工作区slug', on_click=lambda: anythingllm_get_workspaces_list(), color=button_internal_color).style(button_internal_css)
  4447. if config.get("webui", "show_card", "llm", "tongyi"):
  4448. with ui.card().style(card_css):
  4449. ui.label("通义千问/阿里云百炼")
  4450. with ui.row():
  4451. lines = ['web', 'api']
  4452. data_json = {}
  4453. for line in lines:
  4454. data_json[line] = line
  4455. select_tongyi_type = ui.select(
  4456. label='类型',
  4457. options=data_json,
  4458. value=config.get("tongyi", "type")
  4459. ).style("width:100px")
  4460. input_tongyi_cookie_path = ui.input(label='cookie路径', placeholder='web类型下,通义千问登录后,通过浏览器插件Cookie Editor获取Cookie JSON串,然后将数据保存在这个路径的文件中', value=config.get("tongyi", "cookie_path"))
  4461. input_tongyi_cookie_path.style("width:400px")
  4462. with ui.row():
  4463. lines = [
  4464. 'qwen-turbo',
  4465. 'qwen-plus',
  4466. 'qwen-long',
  4467. 'qwen-max-longcontext',
  4468. 'qwen-max',
  4469. 'qwen-max-0428',
  4470. 'baichuan2-turbo',
  4471. 'moonshot-v1-8k',
  4472. 'moonshot-v1-32k',
  4473. 'moonshot-v1-128k',
  4474. 'yi-large',
  4475. 'yi-large-turbo',
  4476. 'yi-medium',
  4477. ]
  4478. data_json = {}
  4479. for line in lines:
  4480. data_json[line] = line
  4481. select_tongyi_model = ui.select(
  4482. label='类型',
  4483. options=data_json,
  4484. value=config.get("tongyi", "model"),
  4485. with_input=True,
  4486. new_value_mode='add-unique',
  4487. clearable=True
  4488. ).style("width:150px")
  4489. input_tongyi_api_key = ui.input(label='密钥', value=config.get("tongyi", "api_key"), placeholder='API类型下,DashScope平台申请的API密钥')
  4490. input_tongyi_preset = ui.input(label='预设', placeholder='API类型下,用于指定一组预定义的设置,以便模型更好地适应特定的对话场景。', value=config.get("tongyi", "preset")).style("width:500px")
  4491. input_tongyi_temperature = ui.input(label='temperature', value=config.get("tongyi", "temperature"), placeholder='控制输出的随机性。').style("width:100px")
  4492. input_tongyi_top_p = ui.input(label='top_p', value=config.get("tongyi", "top_p"), placeholder='在抽样时考虑的标记的最大累积概率。根据其分配的概率对标记进行排序,以仅考虑最可能的标记。Top-k采样直接限制要考虑的标记的最大数量,而Nucleus采样则基于累积概率限制标记的数量。').style("width:100px")
  4493. input_tongyi_top_k = ui.input(label='top_k', value=config.get("tongyi", "top_k"), placeholder='在抽样时考虑的标记的最大数量。Top-k采样考虑一组top_k最有可能的标记。默认值为40。').style("width:100px")
  4494. switch_tongyi_enable_search = ui.switch('联网搜索', value=config.get("tongyi", "enable_search")).style(switch_internal_css)
  4495. with ui.row():
  4496. switch_tongyi_history_enable = ui.switch('上下文记忆', value=config.get("tongyi", "history_enable")).style(switch_internal_css)
  4497. input_tongyi_history_max_len = ui.input(label='最大记忆长度', value=config.get("tongyi", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4498. switch_tongyi_stream = ui.switch('流式输出', value=config.get("tongyi", "stream")).tooltip("是否开启流式输出,开启后,回答会逐句输出,关闭后,回答会一次性输出。")
  4499. if config.get("webui", "show_card", "llm", "gpt4free"):
  4500. with ui.card().style(card_css):
  4501. ui.label("GPT4Free")
  4502. with ui.row():
  4503. providers = [
  4504. "none",
  4505. "g4f.Provider.Bing",
  4506. "g4f.Provider.ChatgptAi",
  4507. "g4f.Provider.Liaobots",
  4508. "g4f.Provider.OpenaiChat",
  4509. "g4f.Provider.Raycast",
  4510. "g4f.Provider.Theb",
  4511. "g4f.Provider.You",
  4512. "g4f.Provider.AItianhuSpace",
  4513. "g4f.Provider.ChatForAi",
  4514. "g4f.Provider.Chatgpt4Online",
  4515. "g4f.Provider.ChatgptNext",
  4516. "g4f.Provider.ChatgptX",
  4517. "g4f.Provider.FlowGpt",
  4518. "g4f.Provider.GptTalkRu",
  4519. "g4f.Provider.Koala",
  4520. ]
  4521. # 将用户配置的值插入list(如果不存在)
  4522. if config.get("gpt4free", "provider") not in providers:
  4523. providers.append(config.get("gpt4free", "provider"))
  4524. data_json = {}
  4525. for line in providers:
  4526. data_json[line] = line
  4527. select_gpt4free_provider = ui.select(
  4528. label='供应商',
  4529. options=data_json,
  4530. value=config.get("gpt4free", "provider"),
  4531. with_input=True,
  4532. new_value_mode='add-unique',
  4533. clearable=True
  4534. )
  4535. input_gpt4free_api_key = ui.input(label='API密钥', placeholder='API KEY,支持代理', value=config.get("gpt4free", "api_key")).style("width:300px;")
  4536. # button_gpt4free_test = ui.button('测试', on_click=lambda: test_openai_key(), color=button_bottom_color).style(button_bottom_css)
  4537. gpt4free_models = [
  4538. "gpt-3.5-turbo",
  4539. "gpt-4",
  4540. "gpt-4-turbo",
  4541. ]
  4542. # 将用户配置的值插入list(如果不存在)
  4543. if config.get("gpt4free", "model") not in gpt4free_models:
  4544. gpt4free_models.append(config.get("gpt4free", "model"))
  4545. data_json = {}
  4546. for line in gpt4free_models:
  4547. data_json[line] = line
  4548. select_gpt4free_model = ui.select(
  4549. label='模型',
  4550. options=data_json,
  4551. value=config.get("gpt4free", "model"),
  4552. with_input=True,
  4553. new_value_mode='add-unique',
  4554. clearable=True
  4555. )
  4556. input_gpt4free_proxy = ui.input(label='HTTP代理地址', placeholder='HTTP代理地址', value=config.get("gpt4free", "proxy")).style("width:300px;")
  4557. with ui.row():
  4558. input_gpt4free_max_tokens = ui.input(label='最大token数', value=config.get("gpt4free", "max_tokens"), placeholder='限制生成回答的最大长度。').style("width:200px;")
  4559. input_gpt4free_preset = ui.input(label='预设', value=config.get("gpt4free", "preset"), placeholder='用于指定一组预定义的设置,以便模型更好地适应特定的对话场景。').style("width:500px")
  4560. switch_gpt4free_history_enable = ui.switch('上下文记忆', value=config.get("gpt4free", "history_enable")).style(switch_internal_css)
  4561. input_gpt4free_history_max_len = ui.input(label='最大记忆长度', value=config.get("gpt4free", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4562. if config.get("webui", "show_card", "llm", "dify"):
  4563. with ui.card().style(card_css):
  4564. ui.label("Dify")
  4565. with ui.row():
  4566. input_dify_api_ip_port = ui.input(
  4567. label="API地址",
  4568. value=config.get("dify", "api_ip_port"),
  4569. placeholder='Dify API地址,从应用的API文档复制过来即可',
  4570. validation={
  4571. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4572. }
  4573. ).style("width:200px;").tooltip('Dify API地址,从应用的API文档复制过来即可')
  4574. input_dify_api_key = ui.input(label='API密钥', value=config.get("dify", "api_key"), placeholder='API密钥,API页面获取').tooltip('API密钥,API页面获取')
  4575. select_dify_type = ui.select(
  4576. label='应用类型',
  4577. options={'聊天助手': '聊天助手'},
  4578. value=config.get("dify", "type")
  4579. ).style("width:200px")
  4580. switch_dify_history_enable = ui.switch('上下文记忆', value=config.get("dify", "history_enable")).style(switch_internal_css)
  4581. if config.get("webui", "show_card", "llm", "volcengine"):
  4582. with ui.card().style(card_css):
  4583. ui.label("火山引擎")
  4584. with ui.row():
  4585. input_volcengine_model = ui.input(label='模型ID', value=config.get("volcengine", "model"), placeholder='推理接入点名称').tooltip('推理接入点名称')
  4586. input_volcengine_api_key = ui.input(label='API密钥', value=config.get("volcengine", "api_key"), placeholder='API密钥,API页面获取').tooltip('API密钥,API页面获取')
  4587. input_volcengine_preset = ui.input(label='预设', value=config.get("volcengine", "preset"), placeholder='用于指定一组预定义的设置,以便模型更好地适应特定的对话场景。').style("width:500px")
  4588. switch_volcengine_history_enable = ui.switch('上下文记忆', value=config.get("volcengine", "history_enable")).style(switch_internal_css)
  4589. input_volcengine_history_max_len = ui.input(label='最大记忆长度', value=config.get("volcengine", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4590. switch_volcengine_stream = ui.switch('流式输出', value=config.get("volcengine", "stream")).style(switch_internal_css)
  4591. if config.get("webui", "show_card", "llm", "custom_llm"):
  4592. with ui.card().style(card_css):
  4593. ui.label("自定义LLM")
  4594. with ui.row():
  4595. textarea_custom_llm_url = ui.textarea(
  4596. label=f"API URL",
  4597. value=config.get("custom_llm", "url"),
  4598. placeholder='发送HTTP请求的API链接',
  4599. validation={
  4600. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4601. }
  4602. ).style("width:200px;").tooltip('发送HTTP请求的API链接')
  4603. textarea_custom_llm_method = ui.select(label=f"API类型", value=config.get("custom_llm", "method"), options={"GET": "GET", "POST": "POST"}).style("width:100px;").tooltip('API类型')
  4604. textarea_custom_llm_headers = ui.textarea(label=f"请求头", value=config.get("custom_llm", "headers"), placeholder='换行分隔,例:Content-Type:application/json\nAuthorization:Bearer sk').style("width:300px;").tooltip('换行分隔,例:Content-Type:application/json\nAuthorization:Bearer sk')
  4605. textarea_custom_llm_proxies = ui.textarea(label=f"代理", value=config.get("custom_llm", "proxies"), placeholder='requests库代理配置方法,json数据用"双引号').style("width:200px;").tooltip('requests库代理配置方法,json数据用"双引号')
  4606. with ui.row():
  4607. select_custom_llm_body_type = ui.select(label=f"请求体类型", value=config.get("custom_llm", "body_type"), options={"json": "json", "raw": "raw"}).style("width:150px;").tooltip('请求体类型')
  4608. textarea_custom_llm_body = ui.textarea(label=f"请求体", value=config.get("custom_llm", "body"), placeholder='请求体,写字符串,注意变量需要两个大括号包裹{{}},json数据的话用"双引号').style("width:300px;").tooltip('请求体,写字符串,注意变量需要两个大括号包裹{{}},json数据的话用"双引号')
  4609. select_custom_llm_resp_data_type = ui.select(label=f"请求返回数据类型", value=config.get("custom_llm", "resp_data_type"), options={"json": "json", "content": "content"}).style("width:150px;").tooltip('请求返回数据类型')
  4610. textarea_custom_llm_data_analysis = ui.textarea(label=f"数据解析(eval执行)", value=config.get("custom_llm", "data_analysis"), placeholder='数据解析,请不要随意修改resp变量,会被用于最后返回数据内容的解析').style("width:300px;").tooltip('数据解析,请不要随意修改resp变量,会被用于最后返回数据内容的解析')
  4611. textarea_custom_llm_resp_template = ui.textarea(label=f"返回内容模板", value=config.get("custom_llm", "resp_template"), placeholder='请不要随意删除data变量,支持动态变量,最终会合并成完成内容进行音频合成').style("width:300px;").tooltip('请不要随意删除data变量,支持动态变量,最终会合并成完成内容进行音频合成')
  4612. if config.get("webui", "show_card", "llm", "llm_tpu"):
  4613. with ui.card().style(card_css):
  4614. ui.label("LLM_TPU")
  4615. with ui.row():
  4616. input_llm_tpu_api_ip_port = ui.input(
  4617. label='API地址',
  4618. value=config.get("llm_tpu", "api_ip_port"),
  4619. placeholder='llm_tpu启动gradio web demo后监听的ip端口地址',
  4620. validation={
  4621. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4622. }
  4623. )
  4624. switch_llm_tpu_history_enable = ui.switch('上下文记忆', value=config.get("llm_tpu", "history_enable")).style(switch_internal_css)
  4625. input_llm_tpu_history_max_len = ui.input(label='最大记忆长度', value=config.get("llm_tpu", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米')
  4626. with ui.row():
  4627. input_llm_tpu_max_length = ui.input(label='max_length', value=config.get("llm_tpu", "max_length"), placeholder='max_length').style("width:200px;")
  4628. input_llm_tpu_temperature = ui.input(label='温度', value=config.get("llm_tpu", "temperature"), placeholder='(0, 1.0] 控制生成文本的随机性。较高的温度值会使生成的文本更随机和多样化,而较低的温度值会使生成的文本更加确定和一致。').style("width:200px;")
  4629. input_llm_tpu_top_p = ui.input(label='前p个选择', value=config.get("llm_tpu", "top_p"), placeholder='[0, 1.0] Nucleus采样。这个参数控制模型从累积概率大于一定阈值的令牌中进行采样。较高的值会产生更多的多样性,较低的值会产生更少但更确定的回答。').style("width:200px;")
  4630. with ui.tab_panel(tts_page).style(tab_panel_css):
  4631. # 通用-合成试听音频
  4632. async def tts_common_audio_synthesis():
  4633. ui.notify(position="top", type="warning", message="音频合成中,将会阻塞其他任务运行,请勿做其他操作,查看日志情况,耐心等待")
  4634. logger.warning("音频合成中,将会阻塞其他任务运行,请勿做其他操作,查看日志情况,耐心等待")
  4635. content = input_tts_common_text.value
  4636. audio_synthesis_type = select_tts_common_audio_synthesis_type.value
  4637. # 使用本地配置进行音频合成,返回音频路径
  4638. file_path = await audio.audio_synthesis_use_local_config(content, audio_synthesis_type)
  4639. if file_path:
  4640. logger.info(f"音频合成成功,存储于:{file_path}")
  4641. ui.notify(position="top", type="positive", message=f"音频合成成功,存储于:{file_path}")
  4642. else:
  4643. logger.error(f"音频合成失败!请查看日志排查问题")
  4644. ui.notify(position="top", type="negative", message=f"音频合成失败!请查看日志排查问题")
  4645. return
  4646. def clear_tts_common_audio_card(file_path):
  4647. tts_common_audio_card.clear()
  4648. if common.del_file(file_path):
  4649. ui.notify(position="top", type="positive", message=f"删除文件成功:{file_path}")
  4650. else:
  4651. ui.notify(position="top", type="negative", message=f"删除文件失败:{file_path}")
  4652. # 清空card
  4653. tts_common_audio_card.clear()
  4654. tmp_label = ui.label(f"音频合成成功,存储于:{file_path}")
  4655. tmp_label.move(tts_common_audio_card)
  4656. audio_tmp = ui.audio(src=file_path)
  4657. audio_tmp.move(tts_common_audio_card)
  4658. button_audio_del = ui.button('删除音频', on_click=lambda: clear_tts_common_audio_card(file_path), color=button_internal_color).style(button_internal_css)
  4659. button_audio_del.move(tts_common_audio_card)
  4660. with ui.card().style(card_css):
  4661. ui.label("合成测试(只是测试,若确认使用此TTS,请前往 通用配置 配置 语音合成)")
  4662. with ui.row():
  4663. select_tts_common_audio_synthesis_type = ui.select(
  4664. label='语音合成',
  4665. options=audio_synthesis_type_options,
  4666. value=config.get("audio_synthesis_type")
  4667. ).style("width:200px;")
  4668. input_tts_common_text = ui.input(label='待合成音频内容', placeholder='此处填写待合成的音频文本内容', value="此处填写待合成的音频文本内容,用于试听效果,类型切换不需要保存即可生效。").style("width:350px;")
  4669. button_tts_common_audio_synthesis = ui.button('试听', on_click=lambda: tts_common_audio_synthesis(), color=button_internal_color).style(button_internal_css)
  4670. tts_common_audio_card = ui.card()
  4671. with tts_common_audio_card.style(card_css):
  4672. with ui.row():
  4673. ui.label("此处显示生成的音频,仅显示最新合成的音频,可以在此操作删除合成的音频")
  4674. if config.get("webui", "show_card", "tts", "edge-tts"):
  4675. with ui.card().style(card_css):
  4676. ui.label("Edge-TTS")
  4677. with ui.row():
  4678. with open('data/edge-tts-voice-list.txt', 'r') as file:
  4679. file_content = file.read()
  4680. # 按行分割内容,并去除每行末尾的换行符
  4681. lines = file_content.strip().split('\n')
  4682. data_json = {}
  4683. for line in lines:
  4684. data_json[line] = line
  4685. select_edge_tts_voice = ui.select(
  4686. label='说话人',
  4687. options=data_json,
  4688. value=config.get("edge-tts", "voice")
  4689. )
  4690. input_edge_tts_rate = ui.input(label='语速增益', placeholder='语速增益 默认是 +0%,可以增减,注意 + - %符合别搞没了,不然会影响语音合成', value=config.get("edge-tts", "rate")).style("width:200px;")
  4691. input_edge_tts_volume = ui.input(label='音量增益', placeholder='音量增益 默认是 +0%,可以增减,注意 + - %符合别搞没了,不然会影响语音合成', value=config.get("edge-tts", "volume")).style("width:200px;")
  4692. if config.get("webui", "show_card", "tts", "vits"):
  4693. with ui.card().style(card_css):
  4694. ui.label("VITS-Simple-API")
  4695. with ui.row():
  4696. select_vits_type = ui.select(
  4697. label='类型',
  4698. options={'vits': 'vits', 'bert_vits2': 'bert_vits2', 'gpt_sovits': 'gpt_sovits'},
  4699. value=config.get("vits", "type")
  4700. ).style("width:200px;")
  4701. input_vits_config_path = ui.input(label='配置文件路径', placeholder='模型配置文件存储路径', value=config.get("vits", "config_path")).style("width:200px;")
  4702. input_vits_api_ip_port = ui.input(
  4703. label='API地址',
  4704. placeholder='vits-simple-api启动后监听的ip端口地址',
  4705. value=config.get("vits", "api_ip_port"),
  4706. validation={
  4707. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4708. }
  4709. ).style("width:300px;")
  4710. with ui.row():
  4711. # input_vits_id = ui.input(label='说话人ID', placeholder='API启动时会给配置文件重新划分id,一般为拼音顺序排列,从0开始', value=config.get("vits", "id")).style("width:200px;")
  4712. select_vits_id = ui.select(
  4713. label='说话人ID',
  4714. options={config.get("vits", "id"): config.get("vits", "id")},
  4715. value=config.get("vits", "id")
  4716. ).style("width:200px;")
  4717. async def vits_get_speaker_id():
  4718. try:
  4719. API_URL = urljoin(input_vits_api_ip_port.value, '/voice/speakers')
  4720. resp_data = await common.send_async_request(API_URL, "GET", resp_data_type="json")
  4721. if resp_data is None:
  4722. content = "vits-simple-api检索说话人失败,请查看双方日志排查问题"
  4723. logger.error(content)
  4724. ui.notify(position="top", type="negative", message=content)
  4725. else:
  4726. content = "vits-simple-api检索说话人成功"
  4727. logger.info(content)
  4728. ui.notify(position="top", type="positive", message=content)
  4729. data_json = {}
  4730. if select_vits_type.value == "vits":
  4731. for vits_info in resp_data["VITS"]:
  4732. data_json[vits_info['id']] = vits_info['name']
  4733. select_vits_id.set_options(data_json, value=int(config.get("vits", "id")))
  4734. elif select_vits_type.value == "bert_vits2":
  4735. for vits_info in resp_data["BERT-VITS2"]:
  4736. data_json[vits_info['id']] = vits_info['name']
  4737. select_vits_id.set_options(data_json, value=int(config.get("vits", "id")))
  4738. elif select_vits_type.value == "gpt_sovits":
  4739. for vits_info in resp_data["GPT-SOVITS"]:
  4740. data_json[vits_info['id']] = vits_info['name']
  4741. select_vits_gpt_sovits_id.set_options(data_json, value=int(config.get("vits", "gpt_sovits", "id")))
  4742. except Exception as e:
  4743. logger.error(traceback.format_exc())
  4744. logger.error(f'vits-simple-api未知错误: {e}')
  4745. ui.notify(position="top", type="negative", message=f'vits-simple-api未知错误: {e}')
  4746. select_vits_lang = ui.select(
  4747. label='语言',
  4748. options={'自动': '自动', '中文': '中文', '英文': '英文', '日文': '日文'},
  4749. value=config.get("vits", "lang")
  4750. ).style("width:100px;")
  4751. input_vits_length = ui.input(label='语音长度', placeholder='调节语音长度,相当于调节语速,该数值越大语速越慢', value=config.get("vits", "length")).style("width:200px;")
  4752. button_vits_get_speaker_id = ui.button('检索说话人', on_click=lambda: vits_get_speaker_id(), color=button_internal_color).style(button_internal_css)
  4753. with ui.row():
  4754. input_vits_noise = ui.input(label='噪声', placeholder='控制感情变化程度', value=config.get("vits", "noise")).style("width:200px;")
  4755. input_vits_noisew = ui.input(label='噪声偏差', placeholder='控制音素发音长度', value=config.get("vits", "noisew")).style("width:200px;")
  4756. input_vits_max = ui.input(label='分段阈值', placeholder='按标点符号分段,加起来大于max时为一段文本。max<=0表示不分段。', value=config.get("vits", "max")).style("width:200px;")
  4757. input_vits_format = ui.input(label='音频格式', placeholder='支持wav,ogg,silk,mp3,flac', value=config.get("vits", "format")).style("width:200px;")
  4758. input_vits_sdp_radio = ui.input(label='SDP/DP混合比', placeholder='SDP/DP混合比:SDP在合成时的占比,理论上此比率越高,合成的语音语调方差越大。', value=config.get("vits", "sdp_radio")).style("width:200px;")
  4759. with ui.expansion('GPT-SOVITS', icon="settings", value=True).classes('w-full'):
  4760. with ui.row():
  4761. select_vits_gpt_sovits_id = ui.select(
  4762. label='说话人ID',
  4763. options={config.get("vits", "gpt_sovits", "id"): config.get("vits", "gpt_sovits", "id")},
  4764. value=config.get("vits", "gpt_sovits", "id")
  4765. ).style("width:200px;")
  4766. select_vits_gpt_sovits_lang = ui.select(
  4767. label='语言',
  4768. options={'auto': '自动', 'zh': '中文', 'jp': '英文', 'en': '日文'},
  4769. value=config.get("vits", "gpt_sovits", "lang")
  4770. ).style("width:100px;")
  4771. input_vits_gpt_sovits_format = ui.input(label='音频格式', value=config.get("vits", "gpt_sovits", "format"), placeholder='支持wav,ogg,silk,mp3,flac').style("width:100px;")
  4772. input_vits_gpt_sovits_segment_size = ui.input(label='segment_size', value=config.get("vits", "gpt_sovits", "segment_size"), placeholder='segment_size').style("width:100px;")
  4773. input_vits_gpt_sovits_reference_audio = ui.input(label='参考音频路径', value=config.get("vits", "gpt_sovits", "reference_audio"), placeholder='参考音频路径').style("width:200px;")
  4774. input_vits_gpt_sovits_prompt_text = ui.input(label='参考音频文本内容', value=config.get("vits", "gpt_sovits", "prompt_text"), placeholder='参考音频文本内容').style("width:200px;")
  4775. select_vits_gpt_sovits_prompt_lang = ui.select(
  4776. label='参考音频语言',
  4777. options={'auto': '自动', 'zh': '中文', 'jp': '英文', 'en': '日文'},
  4778. value=config.get("vits", "gpt_sovits", "prompt_lang")
  4779. ).style("width:150px;")
  4780. with ui.row():
  4781. input_vits_gpt_sovits_top_k = ui.input(label='top_k', value=config.get("vits", "gpt_sovits", "top_k"), placeholder='top_k').style("width:100px;")
  4782. input_vits_gpt_sovits_top_p = ui.input(label='top_p', value=config.get("vits", "gpt_sovits", "top_p"), placeholder='top_p').style("width:100px;")
  4783. input_vits_gpt_sovits_temperature = ui.input(label='temperature', value=config.get("vits", "gpt_sovits", "temperature"), placeholder='temperature').style("width:100px;")
  4784. input_vits_gpt_sovits_preset = ui.input(label='preset', value=config.get("vits", "gpt_sovits", "preset"), placeholder='preset').style("width:100px;")
  4785. if config.get("webui", "show_card", "tts", "bert_vits2"):
  4786. with ui.card().style(card_css):
  4787. ui.label("bert_vits2")
  4788. with ui.row():
  4789. select_bert_vits2_type = ui.select(
  4790. label='类型',
  4791. options={'hiyori': 'hiyori', '刘悦-中文特化API': '刘悦-中文特化API'},
  4792. value=config.get("bert_vits2", "type")
  4793. ).style("width:200px;")
  4794. with ui.expansion('hiyori', icon="settings", value=True).classes('w-full'):
  4795. with ui.row():
  4796. input_bert_vits2_api_ip_port = ui.input(
  4797. label='API地址',
  4798. placeholder='bert_vits2启动后Hiyori UI后监听的ip端口地址',
  4799. value=config.get("bert_vits2", "api_ip_port"),
  4800. validation={
  4801. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4802. }
  4803. ).style("width:300px;")
  4804. input_bert_vits2_model_id = ui.input(label='模型ID', placeholder='给配置文件重新划分id,一般为拼音顺序排列,从0开始', value=config.get("bert_vits2", "model_id")).style("width:200px;")
  4805. input_bert_vits2_speaker_name = ui.input(label='说话人名称', value=config.get("bert_vits2", "speaker_name"), placeholder='配置文件中,对应的说话人的名称').style("width:200px;")
  4806. input_bert_vits2_speaker_id = ui.input(label='说话人ID', value=config.get("bert_vits2", "speaker_id"), placeholder='给配置文件重新划分id,一般为拼音顺序排列,从0开始').style("width:200px;")
  4807. select_bert_vits2_language = ui.select(
  4808. label='语言',
  4809. options={'auto': '自动', 'ZH': '中文', 'JP': '日文', 'EN': '英文'},
  4810. value=config.get("bert_vits2", "language")
  4811. ).style("width:100px;")
  4812. input_bert_vits2_length = ui.input(label='语音长度', placeholder='调节语音长度,相当于调节语速,该数值越大语速越慢', value=config.get("bert_vits2", "length")).style("width:200px;")
  4813. with ui.row():
  4814. input_bert_vits2_noise = ui.input(label='噪声', value=config.get("bert_vits2", "noise"), placeholder='控制感情变化程度').style("width:200px;")
  4815. input_bert_vits2_noisew = ui.input(label='噪声偏差', value=config.get("bert_vits2", "noisew"), placeholder='控制音素发音长度').style("width:200px;")
  4816. input_bert_vits2_sdp_radio = ui.input(label='SDP/DP混合比', value=config.get("bert_vits2", "sdp_radio"), placeholder='SDP/DP混合比:SDP在合成时的占比,理论上此比率越高,合成的语音语调方差越大。').style("width:200px;")
  4817. with ui.row():
  4818. input_bert_vits2_emotion = ui.input(label='emotion', value=config.get("bert_vits2", "emotion"), placeholder='emotion').style("width:200px;")
  4819. input_bert_vits2_style_text = ui.input(label='风格文本', value=config.get("bert_vits2", "style_text"), placeholder='style_text').style("width:200px;")
  4820. input_bert_vits2_style_weight = ui.input(label='风格权重', value=config.get("bert_vits2", "style_weight"), placeholder='主文本和辅助文本的bert混合比率,0表示仅主文本,1表示仅辅助文本0.7').style("width:200px;")
  4821. switch_bert_vits2_auto_translate = ui.switch('自动翻译', value=config.get("bert_vits2", "auto_translate")).style(switch_internal_css)
  4822. switch_bert_vits2_auto_split = ui.switch('自动切分', value=config.get("bert_vits2", "auto_split")).style(switch_internal_css)
  4823. with ui.expansion('刘悦-中文特化API', icon="settings", value=True).classes('w-full'):
  4824. with ui.row():
  4825. input_bert_vits2_liuyue_zh_api_api_ip_port = ui.input(
  4826. label='API地址',
  4827. placeholder='接口服务后监听的ip端口地址',
  4828. value=config.get("bert_vits2", "刘悦-中文特化API", "api_ip_port"),
  4829. validation={
  4830. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4831. }
  4832. ).style("width:300px;")
  4833. input_bert_vits2_liuyue_zh_api_speaker = ui.input(label='说话人名称', value=config.get("bert_vits2", "刘悦-中文特化API", "speaker"), placeholder='配置文件中,对应的说话人的名称').style("width:200px;")
  4834. select_bert_vits2_liuyue_zh_api_language = ui.select(
  4835. label='语言',
  4836. options={'auto': '自动', 'ZH': '中文', 'JP': '日文', 'EN': '英文'},
  4837. value=config.get("bert_vits2", "刘悦-中文特化API", "language")
  4838. ).style("width:100px;")
  4839. input_bert_vits2_liuyue_zh_api_length_scale = ui.input(label='语音长度', placeholder='调节语音长度,相当于调节语速,该数值越大语速越慢', value=config.get("bert_vits2", "刘悦-中文特化API", "length_scale")).style("width:200px;")
  4840. with ui.row():
  4841. input_bert_vits2_liuyue_zh_api_interval_between_para = ui.input(label='interval_between_para', value=config.get("bert_vits2", "刘悦-中文特化API", "interval_between_para"), placeholder='interval_between_para').style("width:200px;")
  4842. input_bert_vits2_liuyue_zh_api_interval_between_sent = ui.input(label='interval_between_sent', value=config.get("bert_vits2", "刘悦-中文特化API", "interval_between_sent"), placeholder='interval_between_sent').style("width:200px;")
  4843. input_bert_vits2_liuyue_zh_api_noise_scale = ui.input(label='噪声', value=config.get("bert_vits2", "刘悦-中文特化API", "noise_scale"), placeholder='控制感情变化程度').style("width:200px;")
  4844. input_bert_vits2_liuyue_zh_api_noise_scale_w = ui.input(label='噪声偏差', value=config.get("bert_vits2", "刘悦-中文特化API", "noise_scale_w"), placeholder='控制音素发音长度').style("width:200px;")
  4845. input_bert_vits2_liuyue_zh_api_sdp_radio = ui.input(label='SDP/DP混合比', value=config.get("bert_vits2", "刘悦-中文特化API", "sdp_radio"), placeholder='SDP/DP混合比:SDP在合成时的占比,理论上此比率越高,合成的语音语调方差越大。').style("width:200px;")
  4846. with ui.row():
  4847. input_bert_vits2_liuyue_zh_api_emotion = ui.input(label='emotion', value=config.get("bert_vits2", "刘悦-中文特化API", "emotion"), placeholder='emotion').style("width:200px;")
  4848. input_bert_vits2_liuyue_zh_api_style_text = ui.input(label='风格文本', value=config.get("bert_vits2", "刘悦-中文特化API", "style_text"), placeholder='style_text').style("width:200px;")
  4849. input_bert_vits2_liuyue_zh_api_style_weight = ui.input(label='风格权重', value=config.get("bert_vits2", "刘悦-中文特化API", "style_weight"), placeholder='主文本和辅助文本的bert混合比率,0表示仅主文本,1表示仅辅助文本0.7').style("width:200px;")
  4850. switch_bert_vits2_cut_by_sent = ui.switch('cut_by_sent', value=config.get("bert_vits2", "刘悦-中文特化API", "cut_by_sent")).style(switch_internal_css)
  4851. if config.get("webui", "show_card", "tts", "vits_fast"):
  4852. with ui.card().style(card_css):
  4853. ui.label("VITS-Fast")
  4854. with ui.row():
  4855. input_vits_fast_config_path = ui.input(label='配置文件路径', placeholder='配置文件的路径,例如:E:\\inference\\finetune_speaker.json', value=config.get("vits_fast", "config_path"))
  4856. input_vits_fast_api_ip_port = ui.input(
  4857. label='API地址',
  4858. placeholder='推理服务运行的链接(需要完整的URL)',
  4859. value=config.get("vits_fast", "api_ip_port"),
  4860. validation={
  4861. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4862. }
  4863. )
  4864. input_vits_fast_character = ui.input(label='说话人', placeholder='选择的说话人,配置文件中的speaker中的其中一个', value=config.get("vits_fast", "character"))
  4865. select_vits_fast_language = ui.select(
  4866. label='语言',
  4867. options={'自动识别': '自动识别', '日本語': '日本語', '简体中文': '简体中文', 'English': 'English', 'Mix': 'Mix'},
  4868. value=config.get("vits_fast", "language")
  4869. )
  4870. input_vits_fast_speed = ui.input(label='语速', placeholder='语速,默认为1', value=config.get("vits_fast", "speed"))
  4871. if config.get("webui", "show_card", "tts", "elevenlabs"):
  4872. with ui.card().style(card_css):
  4873. ui.label("elevenlabs")
  4874. with ui.row():
  4875. input_elevenlabs_api_key = ui.input(label='api密钥', placeholder='elevenlabs密钥,可以不填,默认也有一定额度的免费使用权限,具体多少不知道', value=config.get("elevenlabs", "api_key"))
  4876. input_elevenlabs_voice = ui.input(label='说话人', placeholder='选择的说话人名', value=config.get("elevenlabs", "voice"))
  4877. input_elevenlabs_model = ui.input(label='模型', placeholder='选择的模型', value=config.get("elevenlabs", "model"))
  4878. if config.get("webui", "show_card", "tts", "bark_gui"):
  4879. with ui.card().style(card_css):
  4880. ui.label("bark_gui")
  4881. with ui.row():
  4882. input_bark_gui_api_ip_port = ui.input(label='API地址', placeholder='bark-gui开启webui后监听的IP和端口地址', value=config.get("bark_gui", "api_ip_port")).style("width:200px;")
  4883. input_bark_gui_spk = ui.input(label='说话人', placeholder='选择的说话人,webui的voice中对应的说话人', value=config.get("bark_gui", "spk")).style("width:200px;")
  4884. input_bark_gui_generation_temperature = ui.input(label='生成温度', placeholder='控制合成过程中生成语音的随机性。较高的值(接近1.0)会使输出更加随机,而较低的值(接近0.0)则使其更加确定性和集中。', value=config.get("bark_gui", "generation_temperature")).style("width:200px;")
  4885. input_bark_gui_waveform_temperature = ui.input(label='波形温度', placeholder='类似于generation_temperature,但该参数专门控制从语音模型生成的波形的随机性', value=config.get("bark_gui", "waveform_temperature")).style("width:200px;")
  4886. with ui.row():
  4887. input_bark_gui_end_of_sentence_probability = ui.input(label='句末概率', placeholder='该参数确定在句子结尾添加停顿或间隔的可能性。较高的值会增加停顿的几率,而较低的值则会减少。', value=config.get("bark_gui", "end_of_sentence_probability")).style("width:200px;")
  4888. switch_bark_gui_quick_generation = ui.switch('快速生成', value=config.get("bark_gui", "quick_generation")).style(switch_internal_css)
  4889. input_bark_gui_seed = ui.input(label='随机种子', placeholder='用于随机数生成器的种子值。使用特定的种子确保相同的输入文本每次生成的语音输出都是相同的。值为-1表示将使用随机种子。', value=config.get("bark_gui", "seed")).style("width:200px;")
  4890. input_bark_gui_batch_count = ui.input(label='批量数', placeholder='指定一次批量合成的句子或话语数量。将其设置为1意味着逐句合成一次。', value=config.get("bark_gui", "batch_count")).style("width:200px;")
  4891. if config.get("webui", "show_card", "tts", "vall_e_x"):
  4892. with ui.card().style(card_css):
  4893. ui.label("vall_e_x")
  4894. with ui.row():
  4895. input_vall_e_x_api_ip_port = ui.input(
  4896. label='API地址',
  4897. placeholder='VALL-E-X启动后监听的ip端口地址',
  4898. value=config.get("vall_e_x", "api_ip_port"),
  4899. validation={
  4900. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4901. }
  4902. ).style("width:200px;")
  4903. select_vall_e_x_language = ui.select(
  4904. label='language',
  4905. options={'auto-detect':'auto-detect', 'English':'English', '中文':'中文', '日本語':'日本語', 'Mix':'Mix'},
  4906. value=config.get("vall_e_x", "language")
  4907. ).style("width:200px;")
  4908. select_vall_e_x_accent = ui.select(
  4909. label='accent',
  4910. options={'no-accent':'no-accent', 'English':'English', '中文':'中文', '日本語':'日本語'},
  4911. value=config.get("vall_e_x", "accent")
  4912. ).style("width:200px;")
  4913. input_vall_e_x_voice_preset = ui.input(label='voice preset', placeholder='VALL-E-X说话人预设名(Prompt name)', value=config.get("vall_e_x", "voice_preset")).style("width:300px;")
  4914. input_vall_e_x_voice_preset_file_path = ui.input(label='voice_preset_file_path', placeholder='VALL-E-X说话人预设文件路径(npz)', value=config.get("vall_e_x", "voice_preset_file_path")).style("width:300px;")
  4915. if config.get("webui", "show_card", "tts", "openai_tts"):
  4916. with ui.card().style(card_css):
  4917. ui.label("OpenAI TTS")
  4918. with ui.row():
  4919. select_openai_tts_type = ui.select(
  4920. label='类型',
  4921. options={'api': 'api', 'huggingface': 'huggingface'},
  4922. value=config.get("openai_tts", "type")
  4923. ).style("width:200px;")
  4924. input_openai_tts_api_ip_port = ui.input(
  4925. label='API地址',
  4926. value=config.get("openai_tts", "api_ip_port"),
  4927. placeholder='huggingface上对应项目的API地址',
  4928. validation={
  4929. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4930. }
  4931. ).style("width:200px;")
  4932. with ui.row():
  4933. select_openai_tts_model = ui.select(
  4934. label='模型',
  4935. options={'tts-1': 'tts-1', 'tts-1-hd': 'tts-1-hd'},
  4936. value=config.get("openai_tts", "model")
  4937. ).style("width:200px;")
  4938. select_openai_tts_voice = ui.select(
  4939. label='说话人',
  4940. options={'alloy': 'alloy', 'echo': 'echo', 'fable': 'fable', 'onyx': 'onyx', 'nova': 'nova', 'shimmer': 'shimmer'},
  4941. value=config.get("openai_tts", "voice")
  4942. ).style("width:200px;")
  4943. input_openai_tts_api_key = ui.input(label='api key', value=config.get("openai_tts", "api_key"), placeholder='OpenAI API KEY').style("width:200px;")
  4944. if config.get("webui", "show_card", "tts", "reecho_ai"):
  4945. with ui.card().style(card_css):
  4946. ui.label("睿声AI")
  4947. with ui.row():
  4948. input_reecho_ai_Authorization = ui.input(label='API Key', value=config.get("reecho_ai", "Authorization"), placeholder='API Key').style("width:200px;")
  4949. input_reecho_ai_model = ui.input(label='模型ID', value=config.get("reecho_ai", "model"), placeholder='要使用的模型ID (目前统一为reecho-neural-voice-001)').style("width:200px;")
  4950. input_reecho_ai_voiceId = ui.input(label='角色ID', value=config.get("reecho_ai", "voiceId"), placeholder='要使用的角色ID,必须位于账号的角色列表库中,记得展开详情').style("width:300px;")
  4951. with ui.row():
  4952. number_reecho_ai_randomness = ui.number(label='多样性', value=config.get("reecho_ai", "randomness"), format='%d', min=0, max=100, step=1, placeholder='多样性 (0-100,默认请填写97)').style("width:100px;").tooltip('多样性 (0-100,默认请填写97)')
  4953. number_reecho_ai_stability_boost = ui.number(label='稳定性过滤', value=config.get("reecho_ai", "stability_boost"), format='%d', min=0, max=100, step=1, placeholder='稳定性过滤 (0-100,默认请填写40)').style("width:100px;").tooltip('稳定性过滤 (0-100,默认请填写40)')
  4954. input_reecho_ai_promptId = ui.input(label='角色风格 ID', value=config.get("reecho_ai", "promptId"), placeholder='角色风格 ID (默认为default)').style("width:200px;").tooltip('角色风格 ID (默认为default)')
  4955. number_reecho_ai_probability_optimization = ui.number(label='概率优选', value=config.get("reecho_ai", "probability_optimization"), format='%d', min=0, max=100, step=1, placeholder='概率优选 (0-100,默认请填写99)').style("width:100px;").tooltip('概率优选 (0-100,默认请填写99)')
  4956. switch_reecho_ai_break_clone = ui.switch('减弱风格影响', value=config.get("reecho_ai", "break_clone")).style(switch_internal_css).tooltip('减弱风格影响')
  4957. switch_reecho_ai_flash = ui.switch('加速模式生成', value=config.get("reecho_ai", "flash")).style(switch_internal_css).tooltip('加速模式生成,仅V2.0的模型支持')
  4958. if config.get("webui", "show_card", "tts", "gradio_tts"):
  4959. with ui.card().style(card_css):
  4960. ui.label("Gradio")
  4961. with ui.row():
  4962. textarea_gradio_tts_request_parameters = ui.textarea(label='请求参数', value=config.get("gradio_tts", "request_parameters"), placeholder='一定要注意格式啊!{content}用于替换待合成的文本。\nurl是请求地址;\nfn_index是api对应的索引;\ndata_analysis是数据解析规则,暂时只支持元组和列表数据的index索引,请参考模板进行配置\n键不影响请求,需要注意的是参数顺序需要和API请求保持一致\n那么数据可以用json库将dict转成str,这样再用来配置就可靠很多').style("width:800px;")
  4963. if config.get("webui", "show_card", "tts", "gpt_sovits"):
  4964. with ui.card().style(card_css):
  4965. ui.label("GPT-SoVITS")
  4966. with ui.row():
  4967. select_gpt_sovits_type = ui.select(
  4968. label='API类型',
  4969. options={
  4970. 'api':'api',
  4971. 'api_0322':'api_0322',
  4972. 'api_0706':'api_0706',
  4973. 'v2_api_0821': 'v2_api_0821',
  4974. 'webtts':'WebTTS',
  4975. 'gradio':'gradio旧版',
  4976. 'gradio_0322':'gradio_0322',
  4977. },
  4978. value=config.get("gpt_sovits", "type")
  4979. ).style("width:100px;")
  4980. input_gpt_sovits_gradio_ip_port = ui.input(
  4981. label='Gradio API地址',
  4982. value=config.get("gpt_sovits", "gradio_ip_port"),
  4983. placeholder='官方webui程序启动后gradio监听的地址',
  4984. validation={
  4985. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4986. }
  4987. ).style("width:200px;")
  4988. input_gpt_sovits_api_ip_port = ui.input(
  4989. label='API地址(http)',
  4990. value=config.get("gpt_sovits", "api_ip_port"),
  4991. placeholder='官方API程序启动后监听的地址',
  4992. validation={
  4993. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  4994. }
  4995. ).style("width:200px;")
  4996. input_gpt_sovits_ws_ip_port = ui.input(label='WS地址(gradio)', value=config.get("gpt_sovits", "ws_ip_port"), placeholder='启动TTS推理后,ws的接口地址').style("width:200px;")
  4997. with ui.row():
  4998. input_gpt_sovits_gpt_model_path = ui.input(
  4999. label='GPT模型路径',
  5000. value=config.get("gpt_sovits", "gpt_model_path"),
  5001. placeholder='GPT模型路径,填绝对路径'
  5002. ).style("width:300px;")
  5003. input_gpt_sovits_sovits_model_path = ui.input(label='SOVITS模型路径', value=config.get("gpt_sovits", "sovits_model_path"), placeholder='SOVITS模型路径,填绝对路径').style("width:300px;")
  5004. button_gpt_sovits_set_model = ui.button('加载模型', on_click=lambda: gpt_sovits_set_model(), color=button_internal_color).style(button_internal_css)
  5005. with ui.card().style(card_css):
  5006. ui.label("api")
  5007. with ui.row():
  5008. input_gpt_sovits_ref_audio_path = ui.input(label='参考音频路径', value=config.get("gpt_sovits", "ref_audio_path"), placeholder='参考音频路径,建议填绝对路径').style("width:300px;")
  5009. input_gpt_sovits_prompt_text = ui.input(label='参考音频的文本', value=config.get("gpt_sovits", "prompt_text"), placeholder='参考音频的文本').style("width:200px;")
  5010. select_gpt_sovits_prompt_language = ui.select(
  5011. label='参考音频的语种',
  5012. options={'中文':'中文', '日文':'日文', '英文':'英文'},
  5013. value=config.get("gpt_sovits", "prompt_language")
  5014. ).style("width:150px;")
  5015. select_gpt_sovits_language = ui.select(
  5016. label='需要合成的语种',
  5017. options={'自动识别':'自动识别', '中文':'中文', '日文':'日文', '英文':'英文'},
  5018. value=config.get("gpt_sovits", "language")
  5019. ).style("width:150px;")
  5020. select_gpt_sovits_cut = ui.select(
  5021. label='语句切分',
  5022. options={
  5023. '不切':'不切',
  5024. '凑四句一切':'凑四句一切',
  5025. '凑50字一切':'凑50字一切',
  5026. '按中文句号。切':'按中文句号。切',
  5027. '按英文句号.切':'按英文句号.切',
  5028. '按标点符号切':'按标点符号切'
  5029. },
  5030. value=config.get("gpt_sovits", "cut")
  5031. ).style("width:200px;")
  5032. with ui.card().style(card_css):
  5033. ui.label("api_0322 | gradio_0322")
  5034. with ui.row():
  5035. input_gpt_sovits_api_0322_ref_audio_path = ui.input(label='参考音频路径', value=config.get("gpt_sovits", "api_0322", "ref_audio_path"), placeholder='参考音频路径,建议填绝对路径').style("width:300px;")
  5036. input_gpt_sovits_api_0322_prompt_text = ui.input(label='参考音频的文本', value=config.get("gpt_sovits", "api_0322", "prompt_text"), placeholder='参考音频的文本').style("width:200px;")
  5037. select_gpt_sovits_api_0322_prompt_lang = ui.select(
  5038. label='参考音频的语种',
  5039. options={'中文':'中文', '日文':'日文', '英文':'英文'},
  5040. value=config.get("gpt_sovits", "api_0322", "prompt_lang")
  5041. ).style("width:150px;")
  5042. select_gpt_sovits_api_0322_text_lang = ui.select(
  5043. label='需要合成的语种',
  5044. options={
  5045. '自动识别':'自动识别',
  5046. '中文':'中文',
  5047. '日文':'日文',
  5048. '英文':'英文',
  5049. '中英混合': '中英混合',
  5050. '日英混合': '日英混合',
  5051. '多语种混合': '多语种混合',
  5052. },
  5053. value=config.get("gpt_sovits", "api_0322", "text_lang")
  5054. ).style("width:150px;")
  5055. select_gpt_sovits_api_0322_text_split_method = ui.select(
  5056. label='语句切分',
  5057. options={
  5058. '不切':'不切',
  5059. '凑四句一切':'凑四句一切',
  5060. '凑50字一切':'凑50字一切',
  5061. '按中文句号。切':'按中文句号。切',
  5062. '按英文句号.切':'按英文句号.切',
  5063. '按标点符号切':'按标点符号切'
  5064. },
  5065. value=config.get("gpt_sovits", "api_0322", "text_split_method")
  5066. ).style("width:200px;")
  5067. with ui.row():
  5068. input_gpt_sovits_api_0322_top_k = ui.input(label='top_k', value=config.get("gpt_sovits", "api_0322", "top_k"), placeholder='top_k').style("width:100px;")
  5069. input_gpt_sovits_api_0322_top_p = ui.input(label='top_p', value=config.get("gpt_sovits", "api_0322", "top_p"), placeholder='top_p').style("width:100px;")
  5070. input_gpt_sovits_api_0322_temperature = ui.input(label='temperature', value=config.get("gpt_sovits", "api_0322", "temperature"), placeholder='temperature').style("width:100px;")
  5071. input_gpt_sovits_api_0322_batch_size = ui.input(label='batch_size', value=config.get("gpt_sovits", "api_0322", "batch_size"), placeholder='batch_size').style("width:100px;")
  5072. input_gpt_sovits_api_0322_speed_factor = ui.input(label='speed_factor', value=config.get("gpt_sovits", "api_0322", "speed_factor"), placeholder='speed_factor').style("width:100px;")
  5073. input_gpt_sovits_api_0322_fragment_interval = ui.input(label='分段间隔(秒)', value=config.get("gpt_sovits", "api_0322", "fragment_interval"), placeholder='fragment_interval').style("width:100px;")
  5074. switch_gpt_sovits_api_0322_split_bucket = ui.switch('split_bucket', value=config.get("gpt_sovits", "api_0322", "split_bucket")).style(switch_internal_css)
  5075. switch_gpt_sovits_api_0322_return_fragment = ui.switch('return_fragment', value=config.get("gpt_sovits", "api_0322", "return_fragment")).style(switch_internal_css)
  5076. with ui.card().style(card_css):
  5077. ui.label("api_0706")
  5078. with ui.row():
  5079. input_gpt_sovits_api_0706_refer_wav_path = ui.input(label='参考音频路径', value=config.get("gpt_sovits", "api_0706", "refer_wav_path"), placeholder='参考音频路径,建议填绝对路径').style("width:300px;")
  5080. input_gpt_sovits_api_0706_prompt_text = ui.input(label='参考音频的文本', value=config.get("gpt_sovits", "api_0706", "prompt_text"), placeholder='参考音频的文本').style("width:200px;")
  5081. select_gpt_sovits_api_0706_prompt_language = ui.select(
  5082. label='参考音频的语种',
  5083. options={'中文':'中文', '日文':'日文', '英文':'英文'},
  5084. value=config.get("gpt_sovits", "api_0706", "prompt_language")
  5085. ).style("width:150px;")
  5086. select_gpt_sovits_api_0706_text_language = ui.select(
  5087. label='需要合成的语种',
  5088. options={
  5089. '自动识别':'自动识别',
  5090. '中文':'中文',
  5091. '日文':'日文',
  5092. '英文':'英文',
  5093. '中英混合': '中英混合',
  5094. '日英混合': '日英混合',
  5095. '多语种混合': '多语种混合',
  5096. },
  5097. value=config.get("gpt_sovits", "api_0706", "text_language")
  5098. ).style("width:150px;")
  5099. input_gpt_sovits_api_0706_cut_punc = ui.input(label='文本切分', value=config.get("gpt_sovits", "api_0706", "cut_punc"), placeholder='文本切分符号设定, 符号范围,.;?!、,。?!;:…').style("width:200px;")
  5100. with ui.card().style(card_css):
  5101. ui.label("v2_api_0821")
  5102. with ui.row():
  5103. input_gpt_sovits_v2_api_0821_ref_audio_path = ui.input(label='参考音频路径', value=config.get("gpt_sovits", "v2_api_0821", "ref_audio_path"), placeholder='参考音频路径,建议填绝对路径').style("width:300px;")
  5104. input_gpt_sovits_v2_api_0821_prompt_text = ui.input(label='参考音频的文本', value=config.get("gpt_sovits", "v2_api_0821", "prompt_text"), placeholder='参考音频的文本').style("width:200px;")
  5105. select_gpt_sovits_v2_api_0821_prompt_lang = ui.select(
  5106. label='参考音频的语种',
  5107. options={'zh':'中文', 'ja':'日文', 'en':'英文'},
  5108. value=config.get("gpt_sovits", "v2_api_0821", "prompt_lang")
  5109. ).style("width:150px;")
  5110. select_gpt_sovits_v2_api_0821_text_lang = ui.select(
  5111. label='需要合成的语种',
  5112. options={
  5113. "all_zh": "中文",
  5114. "all_yue": "粤语",
  5115. "en": "英文",
  5116. "all_ja": "日文",
  5117. "all_ko": "韩文",
  5118. "zh": "中英混合",
  5119. "yue": "粤英混合",
  5120. "ja": "日英混合",
  5121. "ko": "韩英混合",
  5122. "auto": "多语种混合", #多语种启动切分识别语种
  5123. "auto_yue": "多语种混合(粤语)",
  5124. },
  5125. value=config.get("gpt_sovits", "v2_api_0821", "text_lang")
  5126. ).style("width:150px;")
  5127. select_gpt_sovits_v2_api_0821_text_split_method = ui.select(
  5128. label='语句切分',
  5129. options={
  5130. 'cut0':'不切',
  5131. 'cut1':'凑四句一切',
  5132. 'cut2':'凑50字一切',
  5133. 'cut3':'按中文句号。切',
  5134. 'cut4':'按英文句号.切',
  5135. 'cut5':'按标点符号切'
  5136. },
  5137. value=config.get("gpt_sovits", "v2_api_0821", "text_split_method")
  5138. ).style("width:200px;")
  5139. with ui.row():
  5140. input_gpt_sovits_v2_api_0821_top_k = ui.input(label='top_k', value=config.get("gpt_sovits", "v2_api_0821", "top_k"), placeholder='top_k').style("width:100px;")
  5141. input_gpt_sovits_v2_api_0821_top_p = ui.input(label='top_p', value=config.get("gpt_sovits", "v2_api_0821", "top_p"), placeholder='top_p').style("width:100px;")
  5142. input_gpt_sovits_v2_api_0821_temperature = ui.input(label='temperature', value=config.get("gpt_sovits", "v2_api_0821", "temperature"), placeholder='temperature').style("width:100px;")
  5143. input_gpt_sovits_v2_api_0821_batch_size = ui.input(label='batch_size', value=config.get("gpt_sovits", "v2_api_0821", "batch_size"), placeholder='batch_size').style("width:100px;")
  5144. input_gpt_sovits_v2_api_0821_batch_threshold = ui.input(label='batch_threshold', value=config.get("gpt_sovits", "v2_api_0821", "batch_threshold"), placeholder='batch_threshold').style("width:100px;")
  5145. switch_gpt_sovits_v2_api_0821_split_bucket = ui.switch('split_bucket', value=config.get("gpt_sovits", "v2_api_0821", "split_bucket")).style(switch_internal_css)
  5146. input_gpt_sovits_v2_api_0821_speed_factor = ui.input(label='speed_factor', value=config.get("gpt_sovits", "v2_api_0821", "speed_factor"), placeholder='speed_factor').style("width:100px;")
  5147. input_gpt_sovits_v2_api_0821_fragment_interval = ui.input(label='分段间隔(秒)', value=config.get("gpt_sovits", "v2_api_0821", "fragment_interval"), placeholder='fragment_interval').style("width:100px;")
  5148. input_gpt_sovits_v2_api_0821_seed = ui.input(label='seed', value=config.get("gpt_sovits", "v2_api_0821", "seed"), placeholder='seed').style("width:100px;")
  5149. input_gpt_sovits_v2_api_0821_media_type = ui.input(label='media_type', value=config.get("gpt_sovits", "v2_api_0821", "media_type"), placeholder='media_type').style("width:100px;")
  5150. switch_gpt_sovits_v2_api_0821_parallel_infer = ui.switch('parallel_infer', value=config.get("gpt_sovits", "v2_api_0821", "parallel_infer")).style(switch_internal_css)
  5151. input_gpt_sovits_v2_api_0821_repetition_penalty = ui.input(label='repetition_penalty', value=config.get("gpt_sovits", "v2_api_0821", "repetition_penalty"), placeholder='repetition_penalty').style("width:100px;")
  5152. with ui.card().style(card_css):
  5153. ui.label("WebTTS相关配置")
  5154. with ui.row():
  5155. select_gpt_sovits_webtts_version = ui.select(
  5156. label='版本',
  5157. options={
  5158. '1':'1',
  5159. '1.4':'1.4',
  5160. '2':'2'
  5161. },
  5162. value=config.get("gpt_sovits", "webtts", "version")
  5163. ).style("width:80px;")
  5164. input_gpt_sovits_webtts_api_ip_port = ui.input(label='API地址', value=config.get("gpt_sovits", "webtts", "api_ip_port"), placeholder='API监听地址').style("width:200px;")
  5165. input_gpt_sovits_webtts_spk = ui.input(label='音色', value=config.get("gpt_sovits", "webtts", "spk"), placeholder='音色').style("width:100px;")
  5166. select_gpt_sovits_webtts_lang = ui.select(
  5167. label='语言',
  5168. options={
  5169. 'zh':'中文',
  5170. 'en':'英文',
  5171. 'jp':'日文'
  5172. },
  5173. value=config.get("gpt_sovits", "webtts", "lang")
  5174. ).style("width:100px;")
  5175. input_gpt_sovits_webtts_speed = ui.input(label='语速', value=config.get("gpt_sovits", "webtts", "speed"), placeholder='语速').style("width:100px;")
  5176. input_gpt_sovits_webtts_emotion = ui.input(label='情感', value=config.get("gpt_sovits", "webtts", "emotion"), placeholder='情感').style("width:100px;")
  5177. if config.get("webui", "show_card", "tts", "clone_voice"):
  5178. with ui.card().style(card_css):
  5179. ui.label("clone-voice")
  5180. with ui.row():
  5181. select_clone_voice_type = ui.select(
  5182. label='API接口类型',
  5183. options={'tts':'tts'},
  5184. value=config.get("clone_voice", "type")
  5185. ).style("width:100px;")
  5186. input_clone_voice_api_ip_port = ui.input(
  5187. label='API地址',
  5188. value=config.get("clone_voice", "api_ip_port"),
  5189. placeholder='官方程序启动后监听的地址',
  5190. validation={
  5191. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5192. }
  5193. ).style("width:200px;")
  5194. with ui.row():
  5195. input_clone_voice_voice = ui.input(label='参考音频路径', value=config.get("clone_voice", "voice"), placeholder='参考音频路径,建议填绝对路径').style("width:200px;")
  5196. select_clone_voice_language = ui.select(
  5197. label='需要合成的语种',
  5198. options={'zh-cn':'中文', 'ja':'日文', 'en':'英文',"ko":'ko',"es":'es',"de":'de',
  5199. "fr":'fr',"it":'it',"tr":'tr',"ru":'ru',"pt":'pt',"pl":'pl',"nl":'nl',"ar":'ar',"hu":'hu',"cs":'cs'},
  5200. value=config.get("clone_voice", "language")
  5201. ).style("width:200px;")
  5202. input_clone_voice_speed = ui.input(label='语速', value=config.get("clone_voice", "speed"), placeholder='语速').style("width:100px;")
  5203. if config.get("webui", "show_card", "tts", "azure_tts"):
  5204. with ui.card().style(card_css):
  5205. ui.label("azure_tts")
  5206. with ui.row():
  5207. input_azure_tts_subscription_key = ui.input(label='密钥', value=config.get("azure_tts", "subscription_key"), placeholder='申请开通服务后,自然就看见了').style("width:200px;")
  5208. input_azure_tts_region = ui.input(label='区域', value=config.get("azure_tts", "region"), placeholder='申请开通服务后,自然就看见了').style("width:200px;")
  5209. input_azure_tts_voice_name = ui.input(label='说话人名', value=config.get("azure_tts", "voice_name"), placeholder='Speech Studio平台试听获取说话人名').style("width:200px;")
  5210. if config.get("webui", "show_card", "tts", "fish_speech"):
  5211. with ui.card().style(card_css):
  5212. ui.label("fish_speech")
  5213. with ui.row():
  5214. select_fish_speech_type = ui.select(
  5215. label='类型',
  5216. options={'api_1.1.0':'api_1.1.0', "web":'在线web', 'api_0.2.0':'api_0.2.0'},
  5217. value=config.get("fish_speech", "type")
  5218. ).style("width:200px;")
  5219. input_fish_speech_api_ip_port = ui.input(
  5220. label='API地址',
  5221. value=config.get("fish_speech", "api_ip_port"),
  5222. placeholder='程序启动后监听的地址',
  5223. validation={
  5224. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5225. }
  5226. ).style("width:200px;")
  5227. with ui.expansion('API_1.1.0', icon="settings", value=True).classes('w-full'):
  5228. with ui.row():
  5229. input_fish_speech_api_1_1_0_reference_text = ui.input(label='参考文本', value=config.get("fish_speech", "api_1.1.0", "reference_text"), placeholder='参考文本').style("width:200px;")
  5230. input_fish_speech_api_1_1_0_reference_audio = ui.input(label='参考音频路径', value=config.get("fish_speech", "api_1.1.0", "reference_audio"), placeholder='参考音频路径').style("width:200px;")
  5231. input_fish_speech_api_1_1_0_max_new_tokens = ui.input(label='每批最大令牌数', value=config.get("fish_speech", "api_1.1.0", "max_new_tokens"), placeholder='每批最大令牌数').style("width:200px;")
  5232. input_fish_speech_api_1_1_0_chunk_length = ui.input(label='chunk_length', value=config.get("fish_speech", "api_1.1.0", "chunk_length"), placeholder='迭代提示长度').style("width:200px;")
  5233. input_fish_speech_api_1_1_0_top_p = ui.input(label='top_p', value=config.get("fish_speech", "api_1.1.0", "top_p"), placeholder='自行查阅').style("width:200px;")
  5234. with ui.row():
  5235. input_fish_speech_api_1_1_0_repetition_penalty = ui.input(label='重复惩罚', value=config.get("fish_speech", "api_1.1.0", "repetition_penalty"), placeholder='重复惩罚').style("width:200px;")
  5236. input_fish_speech_api_1_1_0_temperature = ui.input(label='temperature', value=config.get("fish_speech", "api_1.1.0", "temperature"), placeholder='自行查阅').style("width:200px;")
  5237. input_fish_speech_api_1_1_0_speaker = ui.input(label='说话人', value=config.get("fish_speech", "api_1.1.0", "speaker"), placeholder='说话人名').style("width:200px;")
  5238. input_fish_speech_api_1_1_0_format = ui.input(label='音频格式', value=config.get("fish_speech", "api_1.1.0", "format"), placeholder='音频格式').style("width:200px;")
  5239. with ui.expansion('在线Web配置', icon="settings", value=True).classes('w-full'):
  5240. with ui.row():
  5241. input_fish_speech_web_speaker = ui.input(label='speaker', value=config.get("fish_speech", "web", "speaker"), placeholder='说话人,请从web复制说话人的完整名称').style("width:200px;")
  5242. switch_fish_speech_web_enable_ref_audio = ui.switch('启用参考音频', value=config.get("fish_speech", "web", "enable_ref_audio")).style(switch_internal_css)
  5243. input_fish_speech_web_ref_audio_path = ui.input(label='参考音频路径(云端)', value=config.get("fish_speech", "web", "ref_audio_path"), placeholder='抓wss包,查看参考音频的云端绝对路径').style("width:300px;")
  5244. input_fish_speech_web_ref_text = ui.input(label='参考音频文本', value=config.get("fish_speech", "web", "ref_text"), placeholder='参考音频文本').style("width:300px;")
  5245. switch_fish_speech_enable_ref_audio_update = ui.switch('参考音频过期自动更新', value=config.get("fish_speech", "web", "enable_ref_audio_update")).style(switch_internal_css)
  5246. button_fish_speech_web_get_ref_data = ui.button('随机获取参考音频&文本', on_click=lambda: fish_speech_web_get_ref_data(input_fish_speech_web_speaker.value), color=button_internal_color).style(button_internal_css)
  5247. with ui.row():
  5248. input_fish_speech_web_maximum_tokens_per_batch = ui.input(label='maximum_tokens_per_batch', value=config.get("fish_speech", "web", "maximum_tokens_per_batch"), placeholder='自行查阅').style("width:200px;")
  5249. input_fish_speech_web_iterative_prompt_length = ui.input(label='iterative_prompt_length', value=config.get("fish_speech", "web", "iterative_prompt_length"), placeholder='自行查阅').style("width:200px;")
  5250. input_fish_speech_web_temperature = ui.input(label='temperature', value=config.get("fish_speech", "web", "temperature"), placeholder='自行查阅').style("width:200px;")
  5251. input_fish_speech_web_top_p = ui.input(label='top_p', value=config.get("fish_speech", "web", "top_p"), placeholder='自行查阅').style("width:200px;")
  5252. input_fish_speech_web_repetition_penalty = ui.input(label='repetition_penalty', value=config.get("fish_speech", "web", "repetition_penalty"), placeholder='自行查阅').style("width:200px;")
  5253. with ui.expansion('API_0.2.0', icon="settings", value=False).classes('w-full'):
  5254. input_fish_speech_model_name = ui.input(label='模型名', value=config.get("fish_speech", "model_name"), placeholder='需要加载的模型名').style("width:200px;")
  5255. async def fish_speech_load_model(data):
  5256. import aiohttp
  5257. ui.notify(position="top", type="info", message=f'fish_speech 准备加载模型:{data["model_name"]}')
  5258. API_URL = urljoin(data["api_ip_port"], f'/v1/models/{data["model_name"]}')
  5259. try:
  5260. async with aiohttp.ClientSession() as session:
  5261. async with session.put(API_URL, json=data["model_config"]) as response:
  5262. if response.status == 200:
  5263. ret = await response.json()
  5264. logger.debug(ret)
  5265. if ret["name"] == data["model_name"]:
  5266. logger.info(f'fish_speech模型加载成功: {ret["name"]}')
  5267. ui.notify(position="top", type="positive", message=f'fish_speech模型加载成功: {ret["name"]}')
  5268. return ret
  5269. else:
  5270. logger.error(f'fish_speech模型加载失败')
  5271. ui.notify(position="top", type="negative", message=f'fish_speech模型加载失败')
  5272. return None
  5273. except aiohttp.ClientError as e:
  5274. logger.error(f'fish_speech请求失败: {e}')
  5275. ui.notify(position="top", type="negative", message=f'fish_speech请求失败: {e}')
  5276. except Exception as e:
  5277. logger.error(f'fish_speech未知错误: {e}')
  5278. ui.notify(position="top", type="negative", message=f'fish_speech未知错误: {e}')
  5279. return None
  5280. button_fish_speech_load_model = ui.button('加载模型', on_click=lambda: fish_speech_load_model(config.get("fish_speech")), color=button_internal_color).style(button_internal_css)
  5281. with ui.card().style(card_css):
  5282. ui.label("模型配置")
  5283. with ui.row():
  5284. input_fish_speech_model_config_device = ui.input(label='device', value=config.get("fish_speech", "model_config", "device"), placeholder='自行查阅').style("width:200px;")
  5285. input_fish_speech_model_config_llama_config_name = ui.input(label='config_name', value=config.get("fish_speech", "model_config", "llama", "config_name"), placeholder='自行查阅').style("width:200px;")
  5286. input_fish_speech_model_config_llama_checkpoint_path = ui.input(label='checkpoint_path', value=config.get("fish_speech", "model_config", "llama", "checkpoint_path"), placeholder='自行查阅').style("width:200px;")
  5287. input_fish_speech_model_config_llama_precision = ui.input(label='precision', value=config.get("fish_speech", "model_config", "llama", "precision"), placeholder='自行查阅').style("width:200px;")
  5288. input_fish_speech_model_config_llama_tokenizer = ui.input(label='tokenizer', value=config.get("fish_speech", "model_config", "llama", "tokenizer"), placeholder='自行查阅').style("width:200px;")
  5289. switch_fish_speech_model_config_llama_compile = ui.switch('compile', value=config.get("fish_speech", "model_config", "llama", "compile")).style(switch_internal_css)
  5290. input_fish_speech_model_config_vqgan_config_name = ui.input(label='config_name', value=config.get("fish_speech", "model_config", "vqgan", "config_name"), placeholder='自行查阅').style("width:200px;")
  5291. input_fish_speech_model_config_vqgan_checkpoint_path = ui.input(label='checkpoint_path', value=config.get("fish_speech", "model_config", "vqgan", "checkpoint_path"), placeholder='自行查阅').style("width:200px;")
  5292. with ui.card().style(card_css):
  5293. ui.label("TTS配置")
  5294. with ui.row():
  5295. input_fish_speech_tts_config_prompt_text = ui.input(label='prompt_text', value=config.get("fish_speech", "tts_config", "prompt_text"), placeholder='自行查阅').style("width:200px;")
  5296. input_fish_speech_tts_config_prompt_tokens = ui.input(label='prompt_tokens', value=config.get("fish_speech", "tts_config", "prompt_tokens"), placeholder='自行查阅').style("width:200px;")
  5297. input_fish_speech_tts_config_max_new_tokens = ui.input(label='max_new_tokens', value=config.get("fish_speech", "tts_config", "max_new_tokens"), placeholder='自行查阅').style("width:200px;")
  5298. input_fish_speech_tts_config_top_k = ui.input(label='top_k', value=config.get("fish_speech", "tts_config", "top_k"), placeholder='自行查阅').style("width:200px;")
  5299. input_fish_speech_tts_config_top_p = ui.input(label='top_p', value=config.get("fish_speech", "tts_config", "top_p"), placeholder='自行查阅').style("width:200px;")
  5300. with ui.row():
  5301. input_fish_speech_tts_config_repetition_penalty = ui.input(label='repetition_penalty', value=config.get("fish_speech", "tts_config", "repetition_penalty"), placeholder='自行查阅').style("width:200px;")
  5302. input_fish_speech_tts_config_temperature = ui.input(label='temperature', value=config.get("fish_speech", "tts_config", "temperature"), placeholder='自行查阅').style("width:200px;")
  5303. input_fish_speech_tts_config_order = ui.input(label='order', value=config.get("fish_speech", "tts_config", "order"), placeholder='自行查阅').style("width:200px;")
  5304. input_fish_speech_tts_config_seed = ui.input(label='seed', value=config.get("fish_speech", "tts_config", "seed"), placeholder='自行查阅').style("width:200px;")
  5305. input_fish_speech_tts_config_speaker = ui.input(label='speaker', value=config.get("fish_speech", "tts_config", "speaker"), placeholder='自行查阅').style("width:200px;")
  5306. switch_fish_speech_tts_config_use_g2p = ui.switch('use_g2p', value=config.get("fish_speech", "tts_config", "use_g2p")).style(switch_internal_css)
  5307. if config.get("webui", "show_card", "tts", "chattts"):
  5308. with ui.card().style(card_css):
  5309. ui.label("ChatTTS")
  5310. with ui.row():
  5311. select_chattts_type = ui.select(
  5312. label='类型',
  5313. options={"api": "api", "gradio_0621": "gradio_0621", "gradio": "gradio"},
  5314. value=config.get("chattts", "type")
  5315. ).style("width:150px").tooltip("对接的API类型")
  5316. input_chattts_api_ip_port = ui.input(
  5317. label='API地址',
  5318. value=config.get("chattts", "api_ip_port"),
  5319. placeholder='刘悦佬接口程序启动后api监听的地址',
  5320. validation={
  5321. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5322. }
  5323. ).style("width:200px;").tooltip("对接新版刘悦佬整合包的api接口,填api的地址")
  5324. input_chattts_gradio_ip_port = ui.input(
  5325. label='Gradio API地址',
  5326. value=config.get("chattts", "gradio_ip_port"),
  5327. placeholder='官方webui程序启动后gradio监听的地址',
  5328. validation={
  5329. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5330. }
  5331. ).style("width:200px;").tooltip("对接旧版webui的gradio接口,填webui的地址")
  5332. input_chattts_temperature = ui.input(label='温度', value=config.get("chattts", "temperature"), placeholder='默认:0.3').style("width:100px;").tooltip("Audio temperature,越大越发散,越小越保守")
  5333. input_chattts_audio_seed_input = ui.input(label='声音种子', value=config.get("chattts", "audio_seed_input"), placeholder='默认:-1').style("width:100px;").tooltip("声音种子,-1随机,1女生,4女生,8男生")
  5334. input_chattts_top_p = ui.input(label='top_p', value=config.get("chattts", "top_p"), placeholder='默认:0.7').style("width:100px;").tooltip("top_p")
  5335. input_chattts_top_k = ui.input(label='top_k', value=config.get("chattts", "top_k"), placeholder='默认:20').style("width:100px;").tooltip("top_k")
  5336. input_chattts_text_seed_input = ui.input(label='text_seed_input', value=config.get("chattts", "text_seed_input"), placeholder='默认:42').style("width:100px;").tooltip("text_seed_input")
  5337. switch_chattts_refine_text_flag = ui.switch('refine_text', value=config.get("chattts", "refine_text_flag")).style(switch_internal_css)
  5338. with ui.card().style(card_css):
  5339. ui.label("API相关配置")
  5340. with ui.row():
  5341. input_chattts_api_seed = ui.input(label='声音种子', value=config.get("chattts", "api", "seed"), placeholder='默认:2581').style("width:200px;").tooltip("声音种子")
  5342. input_chattts_api_media_type = ui.input(label='音频格式', value=config.get("chattts", "api", "media_type"), placeholder='默认:wav').style("width:200px;").tooltip("音频格式,没事不建议改")
  5343. if config.get("webui", "show_card", "tts", "cosyvoice"):
  5344. with ui.card().style(card_css):
  5345. ui.label("CosyVoice")
  5346. with ui.row():
  5347. select_cosyvoice_type = ui.select(
  5348. label='类型',
  5349. options={"api_0819": "api_0819", "gradio_0707": "gradio_0707"},
  5350. value=config.get("cosyvoice", "type")
  5351. ).style("width:150px").tooltip("对接的API类型")
  5352. input_cosyvoice_gradio_ip_port = ui.input(
  5353. label='Gradio API地址',
  5354. value=config.get("cosyvoice", "gradio_ip_port"),
  5355. placeholder='官方webui程序启动后gradio监听的地址',
  5356. validation={
  5357. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5358. }
  5359. ).style("width:200px;").tooltip("对接webui的gradio接口,填webui的地址")
  5360. input_cosyvoice_api_ip_port = ui.input(
  5361. label='HTTP API地址',
  5362. value=config.get("cosyvoice", "api_ip_port"),
  5363. placeholder='API程序启动后,API请求地址',
  5364. validation={
  5365. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5366. }
  5367. ).style("width:200px;").tooltip("对接api接口,填api端点地址")
  5368. with ui.row():
  5369. with ui.card().style(card_css):
  5370. ui.label("gradio_0707")
  5371. with ui.row():
  5372. select_cosyvoice_gradio_0707_mode_checkbox_group = ui.select(
  5373. label='推理模式',
  5374. options={'预训练音色': '预训练音色', '3s极速复刻': '3s极速复刻', '跨语种复刻': '跨语种复刻', '自然语言控制': '自然语言控制'},
  5375. value=config.get("cosyvoice", "gradio_0707", "mode_checkbox_group")
  5376. ).style("width:200px;")
  5377. select_cosyvoice_gradio_0707_sft_dropdown = ui.select(
  5378. label='预训练音色',
  5379. options={'中文女': '中文女', '中文男': '中文男', '日语男': '日语男', '粤语女': '粤语女', '英文女': '英文女', '英文男': '英文男', '韩语女': '韩语女'},
  5380. value=config.get("cosyvoice", "gradio_0707", "sft_dropdown")
  5381. ).style("width:100px;")
  5382. input_cosyvoice_gradio_0707_prompt_text = ui.input(label='prompt文本', value=config.get("cosyvoice", "gradio_0707", "prompt_text"), placeholder='').style("width:200px;").tooltip("不用就留空")
  5383. input_cosyvoice_gradio_0707_prompt_wav_upload = ui.input(label='prompt音频路径', value=config.get("cosyvoice", "gradio_0707", "prompt_wav_upload"), placeholder='例如:E:\\1.wav').style("width:200px;").tooltip("不用就留空,例如:E:\\1.wav")
  5384. input_cosyvoice_gradio_0707_instruct_text = ui.input(label='instruct文本', value=config.get("cosyvoice", "gradio_0707", "instruct_text"), placeholder='').style("width:200px;").tooltip("不用就留空")
  5385. input_cosyvoice_gradio_0707_seed = ui.input(label='随机推理种子', value=config.get("cosyvoice", "gradio_0707", "seed"), placeholder='默认:0').style("width:100px;").tooltip("随机推理种子")
  5386. with ui.row():
  5387. with ui.card().style(card_css):
  5388. ui.label("api_0819")
  5389. with ui.row():
  5390. input_cosyvoice_api_0819_speaker = ui.input(label='说话人', value=config.get("cosyvoice", "api_0819", "speaker"), placeholder='').style("width:200px;").tooltip("自行查看")
  5391. input_cosyvoice_api_0819_new = ui.input(label='new', value=config.get("cosyvoice", "api_0819", "new"), placeholder='0').style("width:200px;").tooltip("自行查看")
  5392. input_cosyvoice_api_0819_speed = ui.input(label='语速', value=config.get("cosyvoice", "api_0819", "speed"), placeholder='1').style("width:200px;").tooltip("语速")
  5393. with ui.tab_panel(svc_page).style(tab_panel_css):
  5394. if config.get("webui", "show_card", "svc", "ddsp_svc"):
  5395. with ui.card().style(card_css):
  5396. ui.label("DDSP-SVC")
  5397. with ui.row():
  5398. switch_ddsp_svc_enable = ui.switch('启用', value=config.get("ddsp_svc", "enable")).style(switch_internal_css)
  5399. input_ddsp_svc_config_path = ui.input(label='配置文件路径', placeholder='模型配置文件config.yaml的路径(此处可以不配置,暂时没有用到)', value=config.get("ddsp_svc", "config_path"))
  5400. input_ddsp_svc_config_path.style("width:400px")
  5401. input_ddsp_svc_api_ip_port = ui.input(
  5402. label='API地址',
  5403. placeholder='flask_api服务运行的ip端口,例如:http://127.0.0.1:6844',
  5404. value=config.get("ddsp_svc", "api_ip_port"),
  5405. validation={
  5406. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5407. }
  5408. )
  5409. input_ddsp_svc_api_ip_port.style("width:400px")
  5410. input_ddsp_svc_fSafePrefixPadLength = ui.input(label='安全前缀填充长度', placeholder='安全前缀填充长度,不知道干啥用,默认为0', value=config.get("ddsp_svc", "fSafePrefixPadLength"))
  5411. input_ddsp_svc_fSafePrefixPadLength.style("width:300px")
  5412. with ui.row():
  5413. input_ddsp_svc_fPitchChange = ui.input(label='变调', placeholder='音调设置,默认为0', value=config.get("ddsp_svc", "fPitchChange"))
  5414. input_ddsp_svc_fPitchChange.style("width:300px")
  5415. input_ddsp_svc_sSpeakId = ui.input(label='说话人ID', placeholder='说话人ID,需要和模型数据对应,默认为0', value=config.get("ddsp_svc", "sSpeakId"))
  5416. input_ddsp_svc_sSpeakId.style("width:400px")
  5417. input_ddsp_svc_sampleRate = ui.input(label='采样率', placeholder='DAW所需的采样率,默认为44100', value=config.get("ddsp_svc", "sampleRate"))
  5418. input_ddsp_svc_sampleRate.style("width:300px")
  5419. if config.get("webui", "show_card", "svc", "so_vits_svc"):
  5420. with ui.card().style(card_css):
  5421. ui.label("SO-VITS-SVC")
  5422. with ui.row():
  5423. switch_so_vits_svc_enable = ui.switch('启用', value=config.get("so_vits_svc", "enable")).style(switch_internal_css)
  5424. input_so_vits_svc_config_path = ui.input(label='配置文件路径', placeholder='模型配置文件config.json的路径', value=config.get("so_vits_svc", "config_path"))
  5425. input_so_vits_svc_config_path.style("width:400px")
  5426. with ui.grid(columns=2):
  5427. input_so_vits_svc_api_ip_port = ui.input(
  5428. label='API地址',
  5429. placeholder='flask_api_full_song服务运行的ip端口,例如:http://127.0.0.1:1145',
  5430. value=config.get("so_vits_svc", "api_ip_port"),
  5431. validation={
  5432. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5433. }
  5434. )
  5435. input_so_vits_svc_api_ip_port.style("width:400px")
  5436. input_so_vits_svc_spk = ui.input(label='说话人', placeholder='说话人,需要和配置文件内容对应', value=config.get("so_vits_svc", "spk"))
  5437. input_so_vits_svc_spk.style("width:400px")
  5438. input_so_vits_svc_tran = ui.input(label='音调', placeholder='音调设置,默认为1', value=config.get("so_vits_svc", "tran"))
  5439. input_so_vits_svc_tran.style("width:300px")
  5440. input_so_vits_svc_wav_format = ui.input(label='输出音频格式', placeholder='音频合成后输出的格式', value=config.get("so_vits_svc", "wav_format"))
  5441. input_so_vits_svc_wav_format.style("width:300px")
  5442. with ui.tab_panel(visual_body_page).style(tab_panel_css):
  5443. if config.get("webui", "show_card", "visual_body", "live2d"):
  5444. with ui.card().style(card_css):
  5445. ui.label("Live2D")
  5446. with ui.row():
  5447. switch_live2d_enable = ui.switch('启用', value=config.get("live2d", "enable")).style(switch_internal_css)
  5448. input_live2d_port = ui.input(label='端口', value=config.get("live2d", "port"), placeholder='web服务运行的端口号,默认:12345,范围:0-65535,没事不要乱改就好')
  5449. # input_live2d_name = ui.input(label='模型名', value=config.get("live2d", "name"), placeholder='模型名称,模型存放于Live2D\live2d-model路径下,请注意路径和模型内容是否匹配')
  5450. live2d_names = common.get_folder_names("Live2D/live2d-model") # 路径写死
  5451. logger.info(f"本地Live2D模型名列表:{live2d_names}")
  5452. data_json = {}
  5453. for line in live2d_names:
  5454. data_json[line] = line
  5455. # live2d_model_name = common.get_live2d_model_name("Live2D/js/model_name.js") # 路径写死
  5456. select_live2d_name = ui.select(
  5457. label='模型名',
  5458. options=data_json,
  5459. value=config.get("live2d", "name")
  5460. ).style("width:150px")
  5461. if config.get("webui", "show_card", "visual_body", "EasyAIVtuber"):
  5462. with ui.card().style(card_css):
  5463. ui.label("EasyAIVtuber")
  5464. with ui.row():
  5465. input_EasyAIVtuber_api_ip_port = ui.input(
  5466. label='API地址',
  5467. value=config.get("EasyAIVtuber", "api_ip_port"),
  5468. placeholder='对接EasyAIVtuber应用监听的ip和端口',
  5469. validation={
  5470. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5471. }
  5472. )
  5473. if config.get("webui", "show_card", "visual_body", "digital_human_video_player"):
  5474. with ui.card().style(card_css):
  5475. ui.label("数字人视频播放器")
  5476. with ui.row():
  5477. select_digital_human_video_player_type = ui.select(
  5478. label='类型',
  5479. options={
  5480. "easy_wav2lip": "easy_wav2lip",
  5481. "sadtalker": "sadtalker",
  5482. "genefaceplusplus": "GeneFacePlusPlus",
  5483. "musetalk": "MuseTalk",
  5484. "anitalker": "AniTalker",
  5485. },
  5486. value=config.get("digital_human_video_player", "type")
  5487. ).style("width:150px")
  5488. input_digital_human_video_player_api_ip_port = ui.input(
  5489. label='API地址',
  5490. value=config.get("digital_human_video_player", "api_ip_port"),
  5491. placeholder='对接 数字人视频播放器 监听的ip和端口',
  5492. validation={
  5493. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5494. }
  5495. )
  5496. if config.get("webui", "show_card", "visual_body", "metahuman_stream"):
  5497. with ui.card().style(card_css):
  5498. ui.label("metahuman_stream")
  5499. with ui.row():
  5500. select_metahuman_stream_type = ui.select(
  5501. label='类型',
  5502. options={'ernerf': 'ernerf', 'musetalk': 'musetalk', 'wav2lip': 'wav2lip'},
  5503. value=config.get("metahuman_stream", "type")
  5504. ).style("width:100px;")
  5505. input_metahuman_stream_api_ip_port = ui.input(
  5506. label='API地址',
  5507. value=config.get("metahuman_stream", "api_ip_port"),
  5508. placeholder='metahuman_stream应用启动API后,监听的ip和端口',
  5509. validation={
  5510. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5511. }
  5512. )
  5513. if config.get("webui", "show_card", "visual_body", "live2d_TTS_LLM_GPT_SoVITS_Vtuber"):
  5514. with ui.card().style(card_css):
  5515. ui.label("live2d_TTS_LLM_GPT_SoVITS_Vtuber")
  5516. with ui.row():
  5517. input_live2d_TTS_LLM_GPT_SoVITS_Vtuber_api_ip_port = ui.input(
  5518. label='API地址',
  5519. value=config.get("live2d_TTS_LLM_GPT_SoVITS_Vtuber", "api_ip_port"),
  5520. placeholder='live2d_TTS_LLM_GPT_SoVITS_Vtuber应用启动API后,监听的ip和端口',
  5521. validation={
  5522. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5523. }
  5524. )
  5525. if config.get("webui", "show_card", "visual_body", "xuniren"):
  5526. with ui.card().style(card_css):
  5527. ui.label("xuniren")
  5528. with ui.row():
  5529. input_xuniren_api_ip_port = ui.input(
  5530. label='API地址',
  5531. value=config.get("xuniren", "api_ip_port"),
  5532. placeholder='xuniren应用启动API后,监听的ip和端口',
  5533. validation={
  5534. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5535. }
  5536. )
  5537. if config.get("webui", "show_card", "visual_body", "unity"):
  5538. with ui.card().style(card_css):
  5539. ui.label("Unity")
  5540. with ui.row():
  5541. # switch_unity_enable = ui.switch('启用', value=config.get("unity", "enable")).style(switch_internal_css)
  5542. input_unity_api_ip_port = ui.input(
  5543. label='API地址',
  5544. value=config.get("unity", "api_ip_port"),
  5545. placeholder='对接Unity应用使用的HTTP中转站监听的ip和端口',
  5546. validation={
  5547. '请输入正确格式的URL': lambda value: common.is_url_check(value),
  5548. }
  5549. )
  5550. input_unity_password = ui.input(label='密码', value=config.get("unity", "password"), placeholder='对接Unity应用使用的HTTP中转站的密码')
  5551. with ui.tab_panel(copywriting_page).style(tab_panel_css):
  5552. with ui.row():
  5553. switch_copywriting_auto_play = ui.switch('自动播放', value=config.get("copywriting", "auto_play")).style(switch_internal_css)
  5554. switch_copywriting_random_play = ui.switch('音频随机播放', value=config.get("copywriting", "random_play")).style(switch_internal_css)
  5555. input_copywriting_audio_interval = ui.input(label='音频播放间隔', value=config.get("copywriting", "audio_interval"), placeholder='文案音频播放之间的间隔时间。就是前一个文案播放完成后,到后一个文案开始播放之间的间隔时间。').tooltip('文案音频播放之间的间隔时间。就是前一个文案播放完成后,到后一个文案开始播放之间的间隔时间。')
  5556. input_copywriting_switching_interval = ui.input(label='音频切换间隔', value=config.get("copywriting", "switching_interval"), placeholder='文案音频切换到弹幕音频的切换间隔时间(反之一样)。\n就是在播放文案时,有弹幕触发并合成完毕,此时会暂停文案播放,然后等待这个间隔时间后,再播放弹幕回复音频。').tooltip('n就是在播放文案时,有弹幕触发并合成完毕,此时会暂停文案播放,然后等待这个间隔时间后,再播放弹幕回复音频。')
  5557. with ui.row():
  5558. input_copywriting_index = ui.input(label='文案索引', value="", placeholder='文案组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  5559. button_copywriting_add = ui.button('增加文案组', on_click=copywriting_add, color=button_internal_color).style(button_internal_css)
  5560. button_copywriting_del = ui.button('删除文案组', on_click=lambda: copywriting_del(input_copywriting_index.value), color=button_internal_color).style(button_internal_css)
  5561. copywriting_config_var = {}
  5562. copywriting_config_card = ui.card()
  5563. for index, copywriting_config in enumerate(config.get("copywriting", "config")):
  5564. with copywriting_config_card.style(card_css):
  5565. with ui.row():
  5566. copywriting_config_var[str(5 * index)] = ui.input(label=f"文案存储路径#{index + 1}", value=copywriting_config["file_path"], placeholder='文案文件存储路径。不建议更改。').style("width:200px;").tooltip('文案文件存储路径。不建议更改。')
  5567. copywriting_config_var[str(5 * index + 1)] = ui.input(label=f"音频存储路径#{index + 1}", value=copywriting_config["audio_path"], placeholder='文案音频文件存储路径。不建议更改。').style("width:200px;").tooltip('文案音频文件存储路径。不建议更改。')
  5568. copywriting_config_var[str(5 * index + 2)] = ui.input(label=f"连续播放数#{index + 1}", value=copywriting_config["continuous_play_num"], placeholder='文案播放列表中连续播放的音频文件个数,如果超过了这个个数就会切换下一个文案列表').style("width:200px;").tooltip('文案播放列表中连续播放的音频文件个数,如果超过了这个个数就会切换下一个文案列表')
  5569. copywriting_config_var[str(5 * index + 3)] = ui.input(label=f"连续播放时间#{index + 1}", value=copywriting_config["max_play_time"], placeholder='文案播放列表中连续播放音频的时长,如果超过了这个时长就会切换下一个文案列表').style("width:200px;").tooltip('文案播放列表中连续播放音频的时长,如果超过了这个时长就会切换下一个文案列表')
  5570. copywriting_config_var[str(5 * index + 4)] = ui.textarea(label=f"播放列表#{index + 1}", value=textarea_data_change(copywriting_config["play_list"]), placeholder='此处填写需要播放的音频文件全名,填写完毕后点击 保存配置。文件全名从音频列表中复制,换行分隔,请勿随意填写').style("width:500px;").tooltip('此处填写需要播放的音频文件全名,填写完毕后点击 保存配置。文件全名从音频列表中复制,换行分隔,请勿随意填写')
  5571. with ui.card().style(card_css):
  5572. ui.label("文案音频合成")
  5573. with ui.row():
  5574. input_copywriting_text_path = ui.input(label='文案文本路径', value=config.get("copywriting", "text_path"), placeholder='待合成的文案文本文件的路径').style("width:250px;").tooltip('待合成的文案文本文件的路径')
  5575. button_copywriting_text_load = ui.button('加载文本', on_click=copywriting_text_load, color=button_internal_color).style(button_internal_css)
  5576. input_copywriting_audio_save_path = ui.input(label='音频存储路径', value=config.get("copywriting", "audio_save_path"), placeholder='音频合成后存储的路径').style("width:250px;").tooltip('音频合成后存储的路径')
  5577. # input_copywriting_chunking_stop_time = ui.input(label='断句停顿时长', value=config.get("copywriting", "chunking_stop_time"), placeholder='自动根据标点断句后,2个句子之间的无声时长').style("width:150px;")
  5578. select_copywriting_audio_synthesis_type = ui.select(
  5579. label='语音合成',
  5580. options=audio_synthesis_type_options,
  5581. value=config.get("copywriting", "audio_synthesis_type")
  5582. ).style("width:200px;")
  5583. with ui.row():
  5584. textarea_copywriting_text = ui.textarea(label='文案文本', value='', placeholder='此处对需要合成文案音频的文本内容进行编辑。文案会自动根据逻辑进行切分,然后根据配置合成完整的一个音频文件。').style("width:1000px;").tooltip('此处对需要合成文案音频的文本内容进行编辑。文案会自动根据逻辑进行切分,然后根据配置合成完整的一个音频文件。')
  5585. with ui.row():
  5586. button_copywriting_save_text = ui.button('保存文案', on_click=copywriting_save_text, color=button_internal_color).style(button_internal_css)
  5587. button_copywriting_audio_synthesis = ui.button('合成音频', on_click=lambda: copywriting_audio_synthesis(), color=button_internal_color).style(button_internal_css)
  5588. copywriting_audio_card = ui.card()
  5589. with copywriting_audio_card.style(card_css):
  5590. with ui.row():
  5591. ui.label("此处显示生成的文案音频,仅显示最新合成的文案音频,可以在此操作删除合成的音频")
  5592. with ui.tab_panel(integral_page).style(tab_panel_css):
  5593. with ui.card().style(card_css):
  5594. ui.label("通用")
  5595. with ui.grid(columns=3):
  5596. switch_integral_enable = ui.switch('启用', value=config.get("integral", "enable")).style(switch_internal_css)
  5597. with ui.card().style(card_css):
  5598. ui.label("签到")
  5599. with ui.grid(columns=3):
  5600. switch_integral_sign_enable = ui.switch('启用', value=config.get("integral", "sign", "enable")).style(switch_internal_css)
  5601. input_integral_sign_get_integral = ui.input(label='获得积分数', value=config.get("integral", "sign", "get_integral"), placeholder='签到成功可以获得的积分数,请填写正整数!')
  5602. textarea_integral_sign_cmd = ui.textarea(label='命令', value=textarea_data_change(config.get("integral", "sign", "cmd")), placeholder='弹幕发送以下命令可以触发签到功能,换行分隔命令')
  5603. with ui.card().style(card_css):
  5604. ui.label("文案")
  5605. integral_sign_copywriting_var = {}
  5606. for index, integral_sign_copywriting in enumerate(config.get("integral", "sign", "copywriting")):
  5607. with ui.grid(columns=2):
  5608. integral_sign_copywriting_var[str(2 * index)] = ui.input(label=f"签到数区间#{index}", value=integral_sign_copywriting["sign_num_interval"], placeholder='限制在此区间内的签到数来触发对应的文案,用-号来进行区间划分,包含边界值')
  5609. integral_sign_copywriting_var[str(2 * index + 1)] = ui.textarea(label=f"文案#{index}", value=textarea_data_change(integral_sign_copywriting["copywriting"]), placeholder='在此签到区间内,触发的文案内容,换行分隔').style("width:400px;")
  5610. with ui.card().style(card_css):
  5611. ui.label("礼物")
  5612. with ui.grid(columns=3):
  5613. switch_integral_gift_enable = ui.switch('启用', value=config.get("integral", "gift", "enable")).style(switch_internal_css)
  5614. input_integral_gift_get_integral_proportion = ui.input(label='获得积分比例', value=config.get("integral", "gift", "get_integral_proportion"), placeholder='此比例和礼物真实金额(元)挂钩,默认就是1元=10积分')
  5615. with ui.card().style(card_css):
  5616. ui.label("文案")
  5617. integral_gift_copywriting_var = {}
  5618. for index, integral_gift_copywriting in enumerate(config.get("integral", "gift", "copywriting")):
  5619. with ui.grid(columns=2):
  5620. integral_gift_copywriting_var[str(2 * index)] = ui.input(label=f"礼物价格区间#{index}", value=integral_gift_copywriting["gift_price_interval"], placeholder='限制在此区间内的礼物价格来触发对应的文案,用-号来进行区间划分,包含边界值')
  5621. integral_gift_copywriting_var[str(2 * index + 1)] = ui.textarea(label=f"文案#{index}", value=textarea_data_change(integral_gift_copywriting["copywriting"]), placeholder='在此礼物区间内,触发的文案内容,换行分隔').style("width:400px;")
  5622. with ui.card().style(card_css):
  5623. ui.label("入场")
  5624. with ui.grid(columns=3):
  5625. switch_integral_entrance_enable = ui.switch('启用', value=config.get("integral", "entrance", "enable")).style(switch_internal_css)
  5626. input_integral_entrance_get_integral = ui.input(label='获得积分数', value=config.get("integral", "entrance", "get_integral"), placeholder='签到成功可以获得的积分数,请填写正整数!')
  5627. with ui.card().style(card_css):
  5628. ui.label("文案")
  5629. integral_entrance_copywriting_var = {}
  5630. for index, integral_entrance_copywriting in enumerate(config.get("integral", "entrance", "copywriting")):
  5631. with ui.grid(columns=2):
  5632. integral_entrance_copywriting_var[str(2 * index)] = ui.input(label=f"入场数区间#{index}", value=integral_entrance_copywriting["entrance_num_interval"], placeholder='限制在此区间内的入场数来触发对应的文案,用-号来进行区间划分,包含边界值')
  5633. integral_entrance_copywriting_var[str(2 * index + 1)] = ui.textarea(label=f"文案#{index}", value=textarea_data_change(integral_entrance_copywriting["copywriting"]), placeholder='在此入场区间内,触发的文案内容,换行分隔').style("width:400px;")
  5634. with ui.card().style(card_css):
  5635. ui.label("增删改查")
  5636. with ui.card().style(card_css):
  5637. ui.label("查询")
  5638. with ui.grid(columns=3):
  5639. switch_integral_crud_query_enable = ui.switch('启用', value=config.get("integral", "crud", "query", "enable")).style(switch_internal_css)
  5640. textarea_integral_crud_query_cmd = ui.textarea(label="命令", value=textarea_data_change(config.get("integral", "crud", "query", "cmd")), placeholder='弹幕发送以下命令可以触发查询功能,换行分隔命令')
  5641. textarea_integral_crud_query_copywriting = ui.textarea(label="文案", value=textarea_data_change(config.get("integral", "crud", "query", "copywriting")), placeholder='触发查询功能后返回的文案内容,换行分隔命令').style("width:400px;")
  5642. with ui.tab_panel(talk_page).style(tab_panel_css):
  5643. with ui.row().style("position:fixed; top: 100px; right: 20px;"):
  5644. with ui.expansion('聊天记录', icon="question_answer", value=True):
  5645. scroll_area_chat_box = ui.scroll_area().style("width:500px; height:700px;")
  5646. with ui.row():
  5647. switch_talk_key_listener_enable = ui.switch('启用按键监听', value=config.get("talk", "key_listener_enable")).style(switch_internal_css).tooltip("启用后,可以通过键盘单击下放配置的录音按键,启动语音识别对话功能")
  5648. switch_talk_direct_run_talk = ui.switch('直接语音对话', value=config.get("talk", "direct_run_talk")).style(switch_internal_css).tooltip("如果启用了,将在首次运行时直接进行语音识别,而不需手动点击开始按键。针对有些系统按键无法触发的情况下,配合连续对话和唤醒词使用")
  5649. audio_device_info_list = common.get_all_audio_device_info("in")
  5650. logger.info(f"声卡输入设备={audio_device_info_list}")
  5651. audio_device_info_dict = {str(device['device_index']): device['device_info'] for device in audio_device_info_list}
  5652. logger.debug(f"声卡输入设备={audio_device_info_dict}")
  5653. select_talk_device_index = ui.select(
  5654. label='声卡输入设备',
  5655. options=audio_device_info_dict,
  5656. value=config.get("talk", "device_index")
  5657. ).style("width:300px;").tooltip('这就是语言对话输入的声卡(麦克风),选择你对应的麦克风即可,如果需要监听电脑声卡可以配合虚拟声卡来实现')
  5658. switch_talk_no_recording_during_playback = ui.switch('播放中不进行录音', value=config.get("talk", "no_recording_during_playback")).style(switch_internal_css).tooltip('AI在播放音频的过程中不进行录音,从而防止麦克风和扬声器太近导致的循环录音的问题')
  5659. input_talk_no_recording_during_playback_sleep_interval = ui.input(label='播放中不进行录音的检测间隔(秒)', value=config.get("talk", "no_recording_during_playback_sleep_interval"), placeholder='这个值设置正常不需要太大,因为在启用了“播放中不进行录音”时,不会出现录音到AI说的话的情况,设置太大会导致恢复录音的时间变慢').style("width:200px;").tooltip('这个值设置正常不需要太大,因为不会出现录音到AI说的话的情况')
  5660. input_talk_username = ui.input(label='你的名字', value=config.get("talk", "username"), placeholder='日志中你的名字,暂时没有实质作用').style("width:200px;")
  5661. switch_talk_continuous_talk = ui.switch('连续对话', value=config.get("talk", "continuous_talk")).style(switch_internal_css).tooltip('仅需按一次录音按键,后续就不需要按了,会自动根据沉默阈值切分等待后,继续录音')
  5662. with ui.row():
  5663. data_json = {}
  5664. for line in ["google", "baidu", "faster_whisper", "sensevoice"]:
  5665. data_json[line] = line
  5666. select_talk_type = ui.select(
  5667. label='录音类型',
  5668. options=data_json,
  5669. value=config.get("talk", "type")
  5670. ).style("width:200px;").tooltip('选择使用的STT类型')
  5671. with open('data/keyboard.txt', 'r') as file:
  5672. file_content = file.read()
  5673. # 按行分割内容,并去除每行末尾的换行符
  5674. lines = file_content.strip().split('\n')
  5675. data_json = {}
  5676. for line in lines:
  5677. data_json[line] = line
  5678. select_talk_trigger_key = ui.select(
  5679. label='录音按键',
  5680. options=data_json,
  5681. value=config.get("talk", "trigger_key"),
  5682. with_input=True,
  5683. clearable=True
  5684. ).style("width:200px;").tooltip('按压此按键就可以触发录音了,按一次就行了')
  5685. select_talk_stop_trigger_key = ui.select(
  5686. label='停录按键',
  5687. options=data_json,
  5688. value=config.get("talk", "stop_trigger_key"),
  5689. with_input=True,
  5690. clearable=True
  5691. ).style("width:200px;").tooltip('按压此按键就可以停止录音了,按一次就行了')
  5692. input_talk_volume_threshold = ui.input(label='音量阈值', value=config.get("talk", "volume_threshold"), placeholder='音量阈值,指的是触发录音的起始音量值,请根据自己的麦克风进行微调到最佳').style("width:100px;").tooltip('音量阈值,指的是触发录音的起始音量值,请根据自己的麦克风进行微调到最佳')
  5693. input_talk_silence_threshold = ui.input(label='停录计数', value=config.get("talk", "silence_threshold"), placeholder='停录计数,指的是音量低于起始值的计数,这个值越大,切分音频越慢,即需要等待更长时间才会停止录音,但也不能太小,不然说一半就停了').style("width:100px;").tooltip('沉默阈值,指的是触发停止路径的最低音量值,请根据自己的麦克风进行微调到最佳')
  5694. input_talk_silence_CHANNELS = ui.input(label='CHANNELS', value=config.get("talk", "CHANNELS"), placeholder='录音用的参数').style("width:100px;")
  5695. input_talk_silence_RATE = ui.input(label='RATE', value=config.get("talk", "RATE"), placeholder='录音用的参数').style("width:100px;")
  5696. switch_talk_show_chat_log = ui.switch('聊天记录', value=config.get("talk", "show_chat_log")).style(switch_internal_css)
  5697. with ui.row():
  5698. textarea_talk_chat_box = ui.textarea(label='聊天框-和AI对话', value="", placeholder='此处填写对话内容可以直接进行对话(前面配置好聊天模式,记得运行先)').style("width:500px;").tooltip("此处填写对话内容可以直接进行对话(前面配置好聊天模式,记得运行先)")
  5699. '''
  5700. 聊天页相关的函数
  5701. '''
  5702. # 发送 聊天框内容
  5703. async def talk_chat_box_send():
  5704. global running_flag
  5705. if running_flag != 1:
  5706. ui.notify(position="top", type="info", message="请先点击“一键运行”,然后再进行聊天")
  5707. return
  5708. # 获取用户名和文本内容
  5709. username = input_talk_username.value
  5710. content = textarea_talk_chat_box.value
  5711. # 清空聊天框
  5712. textarea_talk_chat_box.value = ""
  5713. data = {
  5714. "type": "comment",
  5715. "data": {
  5716. "type": "comment",
  5717. "platform": "webui",
  5718. "username": username,
  5719. "content": content
  5720. }
  5721. }
  5722. logger.debug(f"data={data}")
  5723. main_api_ip = "127.0.0.1" if config.get("api_ip") == "0.0.0.0" else config.get("api_ip")
  5724. await common.send_async_request(f'http://{main_api_ip}:{config.get("api_port")}/send', "POST", data)
  5725. # 发送 聊天框内容 进行复读
  5726. async def talk_chat_box_reread(insert_index=-1, type="reread"):
  5727. global running_flag
  5728. if running_flag != 1:
  5729. ui.notify(position="top", type="warning", message="请先点击“一键运行”,然后再进行聊天")
  5730. return
  5731. # 获取用户名和文本内容
  5732. username = input_talk_username.value
  5733. content = textarea_talk_chat_box.value
  5734. # 清空聊天框
  5735. textarea_talk_chat_box.value = ""
  5736. if insert_index == -1:
  5737. data = {
  5738. "type": type,
  5739. "data": {
  5740. "type": type,
  5741. "username": username,
  5742. "content": content
  5743. }
  5744. }
  5745. else:
  5746. data = {
  5747. "type": type,
  5748. "data": {
  5749. "type": type,
  5750. "username": username,
  5751. "content": content,
  5752. "insert_index": insert_index
  5753. }
  5754. }
  5755. if switch_talk_show_chat_log.value:
  5756. show_chat_log_json = {
  5757. "type": "llm",
  5758. "data": {
  5759. "type": type,
  5760. "username": username,
  5761. "content_type": "question",
  5762. "content": content,
  5763. "timestamp": common.get_bj_time(0)
  5764. }
  5765. }
  5766. data_handle_show_chat_log(show_chat_log_json)
  5767. main_api_ip = "127.0.0.1" if config.get("api_ip") == "0.0.0.0" else config.get("api_ip")
  5768. await common.send_async_request(f'http://{main_api_ip}:{config.get("api_port")}/send', "POST", data)
  5769. # 发送 聊天框内容 进行LLM的调教
  5770. async def talk_chat_box_tuning():
  5771. global running_flag
  5772. if running_flag != 1:
  5773. ui.notify(position="top", type="warning", message="请先点击“一键运行”,然后再进行聊天")
  5774. return
  5775. # 获取用户名和文本内容
  5776. username = input_talk_username.value
  5777. content = textarea_talk_chat_box.value
  5778. # 清空聊天框
  5779. textarea_talk_chat_box.value = ""
  5780. data = {
  5781. "type": "tuning",
  5782. "data": {
  5783. "type": "tuning",
  5784. "username": username,
  5785. "content": content
  5786. }
  5787. }
  5788. main_api_ip = "127.0.0.1" if config.get("api_ip") == "0.0.0.0" else config.get("api_ip")
  5789. await common.send_async_request(f'http://{main_api_ip}:{config.get("api_port")}/send', "POST", data)
  5790. button_talk_chat_box_send = ui.button('发送', on_click=lambda: talk_chat_box_send(), color=button_internal_color).style(button_internal_css).tooltip("发送文本给LLM,模拟弹幕触发操作")
  5791. button_talk_chat_box_reread = ui.button('直接复读', on_click=lambda: talk_chat_box_reread(), color=button_internal_color).style(button_internal_css).tooltip("发送文本给内部机制,触发TTS 复读类型的消息")
  5792. button_talk_chat_box_tuning = ui.button('调教', on_click=lambda: talk_chat_box_tuning(), color=button_internal_color).style(button_internal_css).tooltip("发送文本给LLM,但不会进行TTS等操作")
  5793. button_talk_chat_box_reread_first = ui.button('直接复读-插队首', on_click=lambda: talk_chat_box_reread(0, "reread_top_priority"), color=button_internal_color).style(button_internal_css).tooltip("最高优先级 发送文本给内部机制,触发TTS 直接复读类型的消息")
  5794. with ui.expansion('语音唤醒与睡眠', icon="settings", value=True).classes('w-2/3'):
  5795. with ui.row():
  5796. switch_talk_wakeup_sleep_enable = ui.switch('启用', value=config.get("talk", "wakeup_sleep", "enable")).style(switch_internal_css)
  5797. select_talk_wakeup_sleep_mode = ui.select(
  5798. label='唤醒模式',
  5799. options={"长期唤醒": "长期唤醒", "单次唤醒": "单次唤醒"},
  5800. value=config.get("talk", "wakeup_sleep", "mode")
  5801. ).style("width:100px").tooltip("长期唤醒:说完唤醒词后,会触发提示语,后期对话不需要唤醒词;单次唤醒:每次对话都需要携带唤醒词,否则默认保持睡眠,且不会触发提示语")
  5802. textarea_talk_wakeup_sleep_wakeup_word = ui.textarea(label='唤醒词', placeholder='如:管家 多个请换行分隔', value=textarea_data_change(config.get("talk", "wakeup_sleep", "wakeup_word"))).style("width:200px;")
  5803. textarea_talk_wakeup_sleep_sleep_word = ui.textarea(label='睡眠词', placeholder='如:关机 多个请换行分隔', value=textarea_data_change(config.get("talk", "wakeup_sleep", "sleep_word"))).style("width:200px;")
  5804. textarea_talk_wakeup_sleep_wakeup_copywriting = ui.textarea(label='唤醒提示语', placeholder='如:在的 多个请换行分隔', value=textarea_data_change(config.get("talk", "wakeup_sleep", "wakeup_copywriting"))).style("width:300px;")
  5805. textarea_talk_wakeup_sleep_sleep_copywriting = ui.textarea(label='睡眠提示语', placeholder='如:晚安 多个请换行分隔', value=textarea_data_change(config.get("talk", "wakeup_sleep", "sleep_copywriting"))).style("width:300px;")
  5806. with ui.expansion('谷歌', icon="settings", value=False).classes('w-2/3'):
  5807. with ui.grid(columns=1):
  5808. data_json = {}
  5809. for line in ["zh-CN", "en-US", "ja-JP"]:
  5810. data_json[line] = line
  5811. select_talk_google_tgt_lang = ui.select(
  5812. label='目标翻译语言',
  5813. options=data_json,
  5814. value=config.get("talk", "google", "tgt_lang")
  5815. ).style("width:200px")
  5816. with ui.expansion('百度', icon="settings", value=False).classes('w-2/3'):
  5817. with ui.grid(columns=3):
  5818. input_talk_baidu_app_id = ui.input(label='AppID', value=config.get("talk", "baidu", "app_id"), placeholder='百度云 语音识别应用的 AppID')
  5819. input_talk_baidu_api_key = ui.input(label='API Key', value=config.get("talk", "baidu", "api_key"), placeholder='百度云 语音识别应用的 API Key')
  5820. input_talk_baidu_secret_key = ui.input(label='Secret Key', value=config.get("talk", "baidu", "secret_key"), placeholder='百度云 语音识别应用的 Secret Key')
  5821. with ui.expansion('faster_whisper', icon="settings", value=False).classes('w-2/3'):
  5822. with ui.row():
  5823. input_faster_whisper_model_size = ui.input(label='model_size', value=config.get("talk", "faster_whisper", "model_size"), placeholder='Size of the model to use')
  5824. data_json = {}
  5825. for line in ["自动识别", 'af', 'am', 'ar', 'as', 'az', 'ba', 'be', 'bg', 'bn', 'bo', 'br', 'bs', 'ca', 'cs', 'cy', 'da', 'de', 'el', 'en', 'es', 'et', 'eu', 'fa', 'fi', 'fo', 'fr', 'gl', 'gu', 'ha', 'haw', 'he', 'hi', 'hr', 'ht', 'hu', 'hy', 'id', 'is', 'it', 'ja', 'jw', 'ka', 'kk', 'km', 'kn', 'ko', 'la', 'lb', 'ln', 'lo', 'lt', 'lv', 'mg', 'mi', 'mk', 'ml', 'mn', 'mr', 'ms', 'mt', 'my', 'ne', 'nl', 'nn', 'no', 'oc', 'pa', 'pl', 'ps', 'pt', 'ro', 'ru', 'sa', 'sd', 'si', 'sk', 'sl', 'sn', 'so', 'sq', 'sr', 'su', 'sv', 'sw', 'ta', 'te', 'tg', 'th', 'tk', 'tl', 'tr', 'tt', 'uk', 'ur', 'uz', 'vi', 'yi', 'yo', 'zh', 'yue']:
  5826. data_json[line] = line
  5827. select_faster_whisper_language = ui.select(
  5828. label='识别语言',
  5829. options=data_json,
  5830. value=config.get("talk", "faster_whisper", "language")
  5831. ).style("width:200px")
  5832. data_json = {}
  5833. for line in ["cuda", "cpu", "auto"]:
  5834. data_json[line] = line
  5835. select_faster_whisper_device = ui.select(
  5836. label='device',
  5837. options=data_json,
  5838. value=config.get("talk", "faster_whisper", "device")
  5839. ).style("width:200px")
  5840. data_json = {}
  5841. for line in ["float16", "int8_float16", "int8"]:
  5842. data_json[line] = line
  5843. select_faster_whisper_compute_type = ui.select(
  5844. label='compute_type',
  5845. options=data_json,
  5846. value=config.get("talk", "faster_whisper", "compute_type")
  5847. ).style("width:200px")
  5848. input_faster_whisper_download_root = ui.input(label='download_root', value=config.get("talk", "faster_whisper", "download_root"), placeholder='模型下载路径')
  5849. input_faster_whisper_beam_size = ui.input(label='beam_size', value=config.get("talk", "faster_whisper", "beam_size"), placeholder='系统在每个步骤中要考虑的最可能的候选序列数。具有较大的beam_size将使系统产生更准确的结果,但可能需要更多的计算资源;较小的beam_size会减少计算需求,但可能降低结果的准确性。')
  5850. with ui.expansion('SenseVoice', icon="settings", value=False).classes('w-2/3'):
  5851. with ui.row():
  5852. input_sensevoice_asr_model_path = ui.input(label='ASR 模型路径', value=config.get("talk", "sensevoice", "asr_model_path"), placeholder='ASR模型路径').tooltip("ASR模型路径")
  5853. input_sensevoice_vad_model_path = ui.input(label='VAD 模型路径', value=config.get("talk", "sensevoice", "vad_model_path"), placeholder='VAD模型路径').tooltip("VAD模型路径")
  5854. input_sensevoice_vad_max_single_segment_time = ui.input(label='VAD 模型路径', value=config.get("talk", "sensevoice", "vad_max_single_segment_time"), placeholder='VAD单段最大语音时间').tooltip("VAD单段最大语音时间")
  5855. input_sensevoice_vad_device = ui.input(label='device', value=config.get("talk", "sensevoice", "device"), placeholder='使用设备device').tooltip("使用设备device")
  5856. data_json = {}
  5857. for line in ['zh', 'en', 'jp']:
  5858. data_json[line] = line
  5859. select_sensevoice_language = ui.select(
  5860. label='识别语言',
  5861. options=data_json,
  5862. value=config.get("talk", "sensevoice", "language")
  5863. ).style("width:100px")
  5864. input_sensevoice_text_norm = ui.input(label='text_norm', value=config.get("talk", "sensevoice", "text_norm"), placeholder='text_norm').style("width:100px").tooltip("text_norm")
  5865. input_sensevoice_batch_size_s = ui.input(label='batch_size_s', value=config.get("talk", "sensevoice", "batch_size_s"), placeholder='batch_size_s').style("width:100px").tooltip("batch_size_s")
  5866. input_sensevoice_batch_size = ui.input(label='batch_size', value=config.get("talk", "sensevoice", "batch_size"), placeholder='batch_size').style("width:100px").tooltip("batch_size")
  5867. with ui.tab_panel(image_recognition_page).style(tab_panel_css):
  5868. with ui.card().style(card_css):
  5869. async def get_llm_resp(screenshot_path: str, send_to_all: bool=True):
  5870. try:
  5871. # logger.warning(f"screenshot_path={screenshot_path}")
  5872. prompt = input_image_recognition_prompt.value
  5873. if select_image_recognition_model.value == "gemini":
  5874. from utils.gpt_model.gemini import Gemini
  5875. gemini = Gemini(config.get("image_recognition", "gemini"))
  5876. resp_content = gemini.get_resp_with_img(prompt, screenshot_path)
  5877. data = {
  5878. "type": "reread",
  5879. "username": config.get("talk", "username"),
  5880. "content": resp_content,
  5881. "insert_index": -1
  5882. }
  5883. elif select_image_recognition_model.value == "zhipu":
  5884. from utils.gpt_model.zhipu import Zhipu
  5885. zhipu = Zhipu(config.get("image_recognition", "zhipu"))
  5886. resp_content = zhipu.get_resp_with_img(prompt, screenshot_path)
  5887. data = {
  5888. "type": "reread",
  5889. "data": {
  5890. "username": config.get("talk", "username"),
  5891. "content": resp_content,
  5892. "insert_index": -1
  5893. }
  5894. }
  5895. elif select_image_recognition_model.value == "blip":
  5896. from utils.gpt_model.blip import Blip
  5897. blip = Blip(config.get("image_recognition", "blip"))
  5898. resp_content = blip.get_resp_with_img(prompt, screenshot_path)
  5899. data = {
  5900. "type": "reread",
  5901. "data": {
  5902. "username": config.get("talk", "username"),
  5903. "content": resp_content,
  5904. "insert_index": -1
  5905. }
  5906. }
  5907. if send_to_all:
  5908. if data is not None:
  5909. main_api_ip = "127.0.0.1" if config.get("api_ip") == "0.0.0.0" else config.get("api_ip")
  5910. await common.send_async_request(f'http://{main_api_ip}:{config.get("api_port")}/send', "POST", data)
  5911. return data
  5912. except Exception as e:
  5913. logger.error(traceback.format_exc())
  5914. return None
  5915. async def loop_screenshot_toggle_timer(interval_time: float):
  5916. global loop_screenshot_timer_running, loop_screenshot_timer
  5917. async def image_recognition_screenshot_and_send():
  5918. global running_flag
  5919. if running_flag != 1:
  5920. ui.notify(position="top", type="warning", message="请先点击“一键运行”,然后再进行截图识别")
  5921. return
  5922. logger.info(f"触发截图识别")
  5923. # 根据窗口名截图
  5924. screenshot_path = common.capture_window_by_title(input_image_recognition_img_save_path.value, select_image_recognition_screenshot_window_title.value)
  5925. data = await get_llm_resp(screenshot_path)
  5926. if loop_screenshot_timer_running:
  5927. # 如果定时器已经在运行,则停止它
  5928. loop_screenshot_timer.cancel()
  5929. else:
  5930. # 如果定时器未在运行,则启动它
  5931. loop_screenshot_timer = ui.timer(interval=interval_time, callback=lambda: image_recognition_screenshot_and_send()) # 设置定时器,每秒执行一次perform_task函数
  5932. loop_screenshot_timer.activate()
  5933. loop_screenshot_timer_running = not loop_screenshot_timer_running # 更新定时器运行状态
  5934. # 截图并发送LLM
  5935. async def image_recognition_screenshot_and_send(sleep_time: float):
  5936. global running_flag
  5937. if running_flag != 1:
  5938. ui.notify(position="top", type="warning", message="请先点击“一键运行”,然后再进行截图识别")
  5939. return
  5940. logger.info(f"{input_image_recognition_screenshot_delay.value}后触发截图识别")
  5941. ui.notify(position="top", type="positive", message=f"{input_image_recognition_screenshot_delay.value}后触发截图识别")
  5942. await asyncio.sleep(sleep_time)
  5943. # 根据窗口名截图
  5944. screenshot_path = common.capture_window_by_title(input_image_recognition_img_save_path.value, select_image_recognition_screenshot_window_title.value)
  5945. data = await get_llm_resp(screenshot_path)
  5946. # 摄像头截图并发送LLM
  5947. async def image_recognition_cam_screenshot_and_send(sleep_time: float):
  5948. global running_flag
  5949. if running_flag != 1:
  5950. ui.notify(position="top", type="warning", message="请先点击“一键运行”,然后再进行截图识别")
  5951. return
  5952. logger.info(f"{input_image_recognition_cam_screenshot_delay.value}后触发摄像头截图识别")
  5953. ui.notify(position="top", type="positive", message=f"{input_image_recognition_screenshot_delay.value}后触发摄像头截图识别")
  5954. await asyncio.sleep(sleep_time)
  5955. # 根据摄像头索引截图
  5956. screenshot_path = common.capture_image(input_image_recognition_img_save_path.value, int(select_image_recognition_cam_index.value))
  5957. data = await get_llm_resp(screenshot_path)
  5958. ui.label("通用")
  5959. with ui.row():
  5960. button_image_recognition_enable = ui.switch('启用', value=config.get("image_recognition", "enable")).style(switch_internal_css)
  5961. select_image_recognition_model = ui.select(
  5962. label='模型',
  5963. options={'gemini': 'gemini', 'zhipu': '智谱AI', 'blip': 'blip'},
  5964. value=config.get("image_recognition", "model")
  5965. ).style("width:150px")
  5966. input_image_recognition_img_save_path = ui.input(label='截图保存路径', value=config.get("image_recognition", "img_save_path"), placeholder='截图保存路径,支持绝对或相对路径')
  5967. input_image_recognition_prompt = ui.input(label='携带的提示词', value=config.get("image_recognition", "prompt"), placeholder='图片识别时附带的提示词,协同图片获取回答')
  5968. with ui.card().style(card_css):
  5969. ui.label("电脑截图")
  5970. with ui.row():
  5971. window_titles = common.list_visible_windows()
  5972. data_json = {}
  5973. for line in window_titles:
  5974. data_json[line] = line
  5975. select_image_recognition_screenshot_window_title = ui.select(
  5976. label='截图窗口标题',
  5977. options=data_json,
  5978. value=config.get("image_recognition", "screenshot_window_title")
  5979. ).style("width:300px")
  5980. input_image_recognition_screenshot_delay = ui.input(label='N秒后进行截图', value=config.get("image_recognition", "screenshot_delay"), placeholder='截图延迟,方便用户打开对应窗口').style("width:100px")
  5981. button_image_recognition_screenshot_and_send = ui.button('截图并发送', on_click=lambda: image_recognition_screenshot_and_send(float(input_image_recognition_screenshot_delay.value)), color=button_internal_color).style(button_internal_css)
  5982. switch_image_recognition_loop_screenshot_enable = ui.switch('循环截图并发送', value=config.get("image_recognition", "loop_screenshot_enable")).style(switch_internal_css)
  5983. input_image_recognition_loop_screenshot_delay = ui.input(label='N秒后自动截图', value=config.get("image_recognition", "loop_screenshot_delay"), placeholder='自动截图延迟,用户在玩游戏或者看视频等情况下,可以自动触发图像识别').style("width:100px")
  5984. # button_image_recognition_loop_screenshot_and_send = ui.button('循环截图并发送', on_click=lambda: loop_screenshot_toggle_timer(float(input_image_recognition_screenshot_delay.value)), color=button_internal_color).style(button_internal_css)
  5985. with ui.card().style(card_css):
  5986. ui.label("摄像头截图")
  5987. with ui.row():
  5988. switch_image_recognition_cam_screenshot_enable = ui.switch('启用', value=config.get("image_recognition", "cam_screenshot_enable")).style(switch_internal_css)
  5989. if config.get("image_recognition", "cam_screenshot_enable"):
  5990. cam_indexs = common.list_cameras()
  5991. else:
  5992. cam_indexs = []
  5993. data_json = {}
  5994. for line in cam_indexs:
  5995. data_json[line] = line
  5996. select_image_recognition_cam_index = ui.select(
  5997. label='摄像头索引',
  5998. options=data_json,
  5999. value=config.get("image_recognition", "cam_index")
  6000. ).style("width:100px")
  6001. input_image_recognition_cam_screenshot_delay = ui.input(label='N秒后进行截图', value=config.get("image_recognition", "cam_screenshot_delay"), placeholder='截图延迟,方便用户调整摄像头').style("width:100px")
  6002. button_image_recognition_cam_screenshot_and_send = ui.button('截图并发送', on_click=lambda: image_recognition_cam_screenshot_and_send(float(input_image_recognition_cam_screenshot_delay.value)), color=button_internal_color).style(button_internal_css)
  6003. switch_image_recognition_loop_cam_screenshot_enable = ui.switch('循环截图并发送', value=config.get("image_recognition", "loop_cam_screenshot_enable")).style(switch_internal_css)
  6004. input_image_recognition_loop_cam_screenshot_delay = ui.input(label='N秒后自动截图', value=config.get("image_recognition", "loop_cam_screenshot_delay"), placeholder='自动截图延迟,可以自动触发图像识别').style("width:100px")
  6005. with ui.card().style(card_css):
  6006. ui.label("Gemini")
  6007. with ui.row():
  6008. select_image_recognition_gemini_model = ui.select(
  6009. label='模型',
  6010. options={'gemini-pro-vision': 'gemini-pro-vision'},
  6011. value=config.get("image_recognition", "gemini", "model")
  6012. ).style("width:150px")
  6013. input_image_recognition_gemini_api_key = ui.input(label='API Key', value=config.get("image_recognition", "gemini", "api_key"), placeholder='Gemini API KEY')
  6014. input_image_recognition_gemini_http_proxy = ui.input(label='HTTP代理地址', value=config.get("image_recognition", "gemini", "http_proxy"), placeholder='http代理地址,需要魔法才能使用,所以需要配置此项。').style("width:200px;")
  6015. input_image_recognition_gemini_https_proxy = ui.input(label='HTTPS代理地址', value=config.get("image_recognition", "gemini", "https_proxy"), placeholder='https代理地址,需要魔法才能使用,所以需要配置此项。').style("width:200px;")
  6016. with ui.card().style(card_css):
  6017. ui.label("智谱AI")
  6018. with ui.row():
  6019. select_image_recognition_zhipu_model = ui.select(
  6020. label='模型',
  6021. options={'glm-4v': 'glm-4v'},
  6022. value=config.get("image_recognition", "zhipu", "model")
  6023. ).style("width:150px")
  6024. input_image_recognition_zhipu_api_key = ui.input(label='API Key', value=config.get("image_recognition", "zhipu", "api_key"), placeholder='智谱 API KEY')
  6025. with ui.card().style(card_css):
  6026. ui.label("Blip")
  6027. with ui.row():
  6028. select_image_recognition_blip_model = ui.select(
  6029. label='模型',
  6030. options={
  6031. 'Salesforce/blip-image-captioning-large': 'Salesforce/blip-image-captioning-large',
  6032. 'Salesforce/blip-image-captioning-base': 'Salesforce/blip-image-captioning-base',
  6033. },
  6034. value=config.get("image_recognition", "blip", "model")
  6035. ).style("width:300px")
  6036. with ui.tab_panel(assistant_anchor_page).style(tab_panel_css):
  6037. with ui.row():
  6038. switch_assistant_anchor_enable = ui.switch('启用', value=config.get("assistant_anchor", "enable")).style(switch_internal_css)
  6039. input_assistant_anchor_username = ui.input(label='助播名', value=config.get("assistant_anchor", "username"), placeholder='助播的用户名,暂时没啥用')
  6040. select_assistant_anchor_audio_synthesis_type = ui.select(
  6041. label='语音合成',
  6042. options=audio_synthesis_type_options,
  6043. value=config.get("assistant_anchor", "audio_synthesis_type")
  6044. ).style("width:200px;")
  6045. with ui.card().style(card_css):
  6046. ui.label("触发类型")
  6047. with ui.row():
  6048. # 类型列表源自audio_synthesis_handle 音频合成的所支持的type值
  6049. assistant_anchor_type_list = ["comment", "local_qa_audio", "song", "reread", "read_comment", "gift",
  6050. "entrance", "follow", "idle_time_task", "reread_top_priority", "schedule",
  6051. "image_recognition_schedule", "key_mapping", "integral"]
  6052. assistant_anchor_type_mapping = {
  6053. "comment": "弹幕",
  6054. "local_qa_audio": "本地问答-音频",
  6055. "song": "点歌",
  6056. "reread": "复读",
  6057. "read_comment": "念弹幕",
  6058. "gift": "礼物",
  6059. "entrance": "入场",
  6060. "follow": "关注",
  6061. "idle_time_task": "闲时任务",
  6062. "reread_top_priority": "最高优先级-复读",
  6063. "schedule": "定时任务",
  6064. "image_recognition_schedule": "图像识别定时任务",
  6065. "key_mapping": "按键映射",
  6066. "integral": "积分",
  6067. }
  6068. assistant_anchor_type_var = {}
  6069. for index, assistant_anchor_type in enumerate(assistant_anchor_type_list):
  6070. if assistant_anchor_type in config.get("assistant_anchor", "type"):
  6071. assistant_anchor_type_var[str(index)] = ui.checkbox(text=assistant_anchor_type_mapping[assistant_anchor_type], value=True)
  6072. else:
  6073. assistant_anchor_type_var[str(index)] = ui.checkbox(text=assistant_anchor_type_mapping[assistant_anchor_type], value=False)
  6074. with ui.grid(columns=4):
  6075. switch_assistant_anchor_local_qa_text_enable = ui.switch('启用文本匹配', value=config.get("assistant_anchor", "local_qa", "text", "enable")).style(switch_internal_css)
  6076. select_assistant_anchor_local_qa_text_format = ui.select(
  6077. label='存储格式',
  6078. options={'json': '自定义json', 'text': '一问一答'},
  6079. value=config.get("assistant_anchor", "local_qa", "text", "format")
  6080. )
  6081. input_assistant_anchor_local_qa_text_file_path = ui.input(label='文本问答数据路径', value=config.get("assistant_anchor", "local_qa", "text", "file_path"), placeholder='本地问答文本数据存储路径').style("width:200px;")
  6082. input_assistant_anchor_local_qa_text_similarity = ui.input(label='文本最低相似度', value=config.get("assistant_anchor", "local_qa", "text", "similarity"), placeholder='最低文本匹配相似度,就是说用户发送的内容和本地问答库中设定的内容的最低相似度。\n低了就会被当做一般弹幕处理').style("width:200px;")
  6083. with ui.grid(columns=4):
  6084. switch_assistant_anchor_local_qa_audio_enable = ui.switch('启用音频匹配', value=config.get("assistant_anchor", "local_qa", "audio", "enable")).style(switch_internal_css)
  6085. select_assistant_anchor_local_qa_audio_type = ui.select(
  6086. label='匹配算法',
  6087. options={'包含关系': '包含关系', '相似度匹配': '相似度匹配'},
  6088. value=config.get("assistant_anchor", "local_qa", "audio", "type")
  6089. )
  6090. input_assistant_anchor_local_qa_audio_file_path = ui.input(label='音频存储路径', value=config.get("assistant_anchor", "local_qa", "audio", "file_path"), placeholder='本地问答音频文件存储路径').style("width:200px;")
  6091. input_assistant_anchor_local_qa_audio_similarity = ui.input(label='音频最低相似度', value=config.get("assistant_anchor", "local_qa", "audio", "similarity"), placeholder='最低音频匹配相似度,就是说用户发送的内容和本地音频库中音频文件名的最低相似度。\n低了就会被当做一般弹幕处理').style("width:200px;")
  6092. with ui.tab_panel(translate_page).style(tab_panel_css):
  6093. with ui.row():
  6094. switch_translate_enable = ui.switch('启用', value=config.get("translate", "enable")).style(switch_internal_css)
  6095. select_translate_type = ui.select(
  6096. label='类型',
  6097. options={'baidu': '百度翻译', 'google': '谷歌翻译'},
  6098. value=config.get("translate", "type")
  6099. ).style("width:100px;")
  6100. select_translate_trans_type = ui.select(
  6101. label='翻译类型',
  6102. options={'弹幕': '弹幕', '回复': '回复', '弹幕+回复': '弹幕+回复'},
  6103. value=config.get("translate", "trans_type")
  6104. ).style("width:150px;")
  6105. with ui.card().style(card_css):
  6106. ui.label("百度翻译")
  6107. with ui.row():
  6108. input_translate_baidu_appid = ui.input(label='APP ID', value=config.get("translate", "baidu", "appid"), placeholder='翻译开放平台 开发者中心 APP ID')
  6109. input_translate_baidu_appkey = ui.input(label='密钥', value=config.get("translate", "baidu", "appkey"), placeholder='翻译开放平台 开发者中心 密钥')
  6110. select_translate_baidu_from_lang = ui.select(
  6111. label='源语言',
  6112. options={'auto': '自动检测', 'zh': '中文', 'cht': '繁体中文', 'en': '英文', 'jp': '日文', 'kor': '韩文', 'yue': '粤语', 'wyw': '文言文'},
  6113. value=config.get("translate", "baidu", "from_lang")
  6114. ).style("width:100px;")
  6115. select_translate_baidu_to_lang = ui.select(
  6116. label='目标语言',
  6117. options={'zh': '中文', 'cht': '繁体中文', 'en': '英文', 'jp': '日文', 'kor': '韩文', 'yue': '粤语', 'wyw': '文言文'},
  6118. value=config.get("translate", "baidu", "to_lang")
  6119. ).style("width:100px;")
  6120. with ui.card().style(card_css):
  6121. ui.label("谷歌翻译")
  6122. with ui.row():
  6123. input_translate_google_proxy = ui.input(label='代理地址', value=config.get("translate", "google", "proxy"), placeholder='代理的完整地址,请携带协议')
  6124. select_translate_google_src_lang = ui.select(
  6125. label='源语言',
  6126. options={'auto': '自动', 'zh-CN': '中文', 'en': '英文', 'ja': '日文'},
  6127. value=config.get("translate", "google", "src_lang")
  6128. ).style("width:100px;")
  6129. select_translate_google_tgt_lang = ui.select(
  6130. label='目标语言',
  6131. options={'zh-CN': '中文', 'en': '英文', 'ja': '日文'},
  6132. value=config.get("translate", "google", "tgt_lang")
  6133. ).style("width:100px;")
  6134. with ui.tab_panel(serial_page).style(tab_panel_css):
  6135. with ui.element('div').classes('p-2 bg-blue-100'):
  6136. ui.label("此页完成串口配置后,可以在“通用配置”的 串口映射配置功能\n 注意!!!此处连接测试完成后,请 关闭串口,因为程序是跨进程使用的,所以不关掉会占用串口,导致无法正常使用!")
  6137. with ui.row():
  6138. input_serial_config_index = ui.input(label='串口配置索引', value="", placeholder='串口配置组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  6139. button_serial_config_add = ui.button('增加串口配置组', on_click=serial_config_add, color=button_internal_color).style(button_internal_css)
  6140. button_serial_config_del = ui.button('删除串口配置组', on_click=lambda: serial_config_del(input_serial_config_index.value), color=button_internal_color).style(button_internal_css)
  6141. serial_config_var = {}
  6142. serial_config_card = ui.card()
  6143. # 刷新串口列表
  6144. async def refresh_serial(index: int):
  6145. logger.warning(index)
  6146. try:
  6147. from utils.serial_manager_instance import get_serial_manager
  6148. serial_manager = get_serial_manager()
  6149. list_ports = await serial_manager.list_ports()
  6150. logger.info(f"搜索到的串口:{list_ports}")
  6151. ui.notify(position="top", type="positive", message=f"搜索到的串口:{list_ports}")
  6152. serial_config_var[str(8 * index)].set_options(list_ports)
  6153. except Exception as e:
  6154. logger.error(traceback.format_exc())
  6155. ui.notify(position="top", type="negative", message=f"{traceback.format_exc()}")
  6156. async def connect_serial(index: int):
  6157. logger.warning(index)
  6158. try:
  6159. from utils.serial_manager_instance import get_serial_manager
  6160. serial_manager = get_serial_manager()
  6161. serial_name = serial_config_var[str(8 * index)].value
  6162. baudrate = serial_config_var[str(8 * index + 1)].value
  6163. resp_json = await serial_manager.connect(serial_name, baudrate)
  6164. if resp_json['ret']:
  6165. ui.notify(position="top", type="positive", message=f"{resp_json['msg']}")
  6166. else:
  6167. ui.notify(position="top", type="negative", message=f"{resp_json['msg']}")
  6168. except Exception as e:
  6169. logger.error(traceback.format_exc())
  6170. ui.notify(position="top", type="negative", message=f"{traceback.format_exc()}")
  6171. async def disconnect_serial(index: int):
  6172. logger.warning(index)
  6173. try:
  6174. from utils.serial_manager_instance import get_serial_manager
  6175. serial_manager = get_serial_manager()
  6176. serial_name = serial_config_var[str(8 * index)].value
  6177. resp_json = await serial_manager.disconnect(serial_name)
  6178. if resp_json['ret']:
  6179. ui.notify(position="top", type="positive", message=f"{resp_json['msg']}")
  6180. else:
  6181. ui.notify(position="top", type="negative", message=f"{resp_json['msg']}")
  6182. except Exception as e:
  6183. logger.error(traceback.format_exc())
  6184. ui.notify(position="top", type="negative", message=f"{traceback.format_exc()}")
  6185. async def send_data_to_serial(index: int):
  6186. try:
  6187. from utils.serial_manager_instance import get_serial_manager
  6188. serial_manager = get_serial_manager()
  6189. serial_name = serial_config_var[str(8 * index)].value
  6190. serial_data_type = serial_config_var[str(8 * index + 5)].value
  6191. send_data = serial_config_var[str(8 * index + 6)].value
  6192. resp_json = await serial_manager.send_data(serial_name, send_data, serial_data_type)
  6193. if resp_json['ret']:
  6194. ui.notify(position="top", type="positive", message=f"{resp_json['msg']}")
  6195. else:
  6196. ui.notify(position="top", type="negative", message=f"{resp_json['msg']}")
  6197. except Exception as e:
  6198. logger.error(traceback.format_exc())
  6199. ui.notify(position="top", type="negative", message=f"{traceback.format_exc()}")
  6200. for index, serial_config in enumerate(config.get("serial", "config")):
  6201. with serial_config_card.style(card_css):
  6202. with ui.row():
  6203. serial_config_var[str(8 * index)] = ui.select(label=f"串口名#{index + 1}", value=serial_config["serial_name"], options={f'{serial_config["serial_name"]}': f'{serial_config["serial_name"]}'}).style("width:200px;").tooltip('文案文件存储路径。不建议更改。')
  6204. serial_config_var[str(8 * index + 1)] = ui.select(
  6205. label=f"波特率#{index + 1}",
  6206. value=serial_config["baudrate"],
  6207. options={'9600': '9600', '19200': '19200', '38400': '38400', '115200': '115200'}
  6208. ).style("width:200px;").tooltip('波特率')
  6209. # TODO:这里的传参一直是0,index值有问题,bug待定位
  6210. serial_config_var[str(8 * index + 2)] = ui.button('刷新串口', on_click=lambda idx=index: refresh_serial(idx))
  6211. serial_config_var[str(8 * index + 3)] = ui.button('打开串口', on_click=lambda idx=index: connect_serial(idx))
  6212. serial_config_var[str(8 * index + 4)] = ui.button('关闭串口', on_click=lambda idx=index: disconnect_serial(idx))
  6213. serial_config_var[str(8 * index + 5)] = ui.select(label=f"发送数据类型#{index + 1}", value=serial_config["serial_data_type"], options={'ASCII': 'ASCII', 'HEX': 'HEX'},).style("width:100px;").tooltip('发送的数据类型')
  6214. serial_config_var[str(8 * index + 6)] = ui.input(label=f"发送数据#{index + 1}", value="", placeholder='填要发的内容,连接后,点 发送').style("width:200px;").tooltip('填要发的内容,连接后,点 发送')
  6215. serial_config_var[str(8 * index + 7)] = ui.button('发送', on_click=lambda idx=index: send_data_to_serial(idx))
  6216. with ui.tab_panel(data_analysis_page).style(tab_panel_css):
  6217. from utils.data_analysis import Data_Analysis
  6218. data_analysis = Data_Analysis(config_path)
  6219. data_analysis_comment_word_cloud_card = ui.card()
  6220. with data_analysis_comment_word_cloud_card.style("width:100%;"):
  6221. echart_comment_word_cloud = ui.echart(data_analysis.get_comment_word_cloud_option(
  6222. int(config.get("data_analysis", "comment_word_cloud", "top_num")))
  6223. ).style(echart_css)
  6224. with ui.row():
  6225. input_data_analysis_comment_word_cloud_top_num = ui.input(label='前N个关键词', value=config.get("data_analysis", "comment_word_cloud", "top_num"), placeholder='筛选前N个弹幕关键词做为词云数据')
  6226. def update_echart_comment_word_cloud():
  6227. data_analysis_comment_word_cloud_card.remove(0)
  6228. echart_comment_word_cloud = ui.echart(data_analysis.get_comment_word_cloud_option(
  6229. int(input_data_analysis_comment_word_cloud_top_num.value))
  6230. ).style(echart_css)
  6231. echart_comment_word_cloud.move(data_analysis_comment_word_cloud_card, 0)
  6232. ui.button('更新数据', on_click=lambda: update_echart_comment_word_cloud())
  6233. data_analysis_integral_card = ui.card()
  6234. with data_analysis_integral_card.style("width:100%;"):
  6235. echart_integral = ui.echart(data_analysis.get_integral_option(
  6236. "integral", int(config.get("data_analysis", "integral", "top_num")))
  6237. ).style(echart_css)
  6238. with ui.row():
  6239. input_data_analysis_integral_top_num = ui.input(label='Top N个数据', value=config.get("data_analysis", "integral", "top_num"), placeholder='筛选Top N个数据')
  6240. def update_echart_integral(type):
  6241. data_analysis_integral_card.remove(0)
  6242. echart_integral = ui.echart(data_analysis.get_integral_option(
  6243. type,
  6244. int(input_data_analysis_integral_top_num.value))
  6245. ).style(echart_css)
  6246. echart_integral.move(data_analysis_integral_card, 0)
  6247. ui.button('获取积分榜', on_click=lambda: update_echart_integral('integral'))
  6248. ui.button('获取观看榜', on_click=lambda: update_echart_integral('view_num'))
  6249. ui.button('获取签到榜', on_click=lambda: update_echart_integral('sign_num'))
  6250. ui.button('获取金额榜', on_click=lambda: update_echart_integral('total_price'))
  6251. data_analysis_gift_card = ui.card()
  6252. with data_analysis_gift_card.style("width:100%;"):
  6253. echart_gift = ui.echart(data_analysis.get_gift_option(int(config.get("data_analysis", "gift", "top_num")))).style(echart_css)
  6254. with ui.row():
  6255. input_data_analysis_gift_top_num = ui.input(label='Top N个数据', value=config.get("data_analysis", "gift", "top_num"), placeholder='筛选Top N个数据')
  6256. def update_echart_gift():
  6257. data_analysis_gift_card.remove(0)
  6258. echart_gift = ui.echart(data_analysis.get_gift_option(
  6259. int(input_data_analysis_gift_top_num.value))
  6260. ).style(echart_css)
  6261. echart_gift.move(data_analysis_gift_card, 0)
  6262. ui.button('更新数据', on_click=lambda: update_echart_gift())
  6263. with ui.tab_panel(web_page).style(tab_panel_css):
  6264. with ui.card().style(card_css):
  6265. ui.label("webui配置")
  6266. with ui.row():
  6267. input_webui_title = ui.input(label='标题', placeholder='webui的标题', value=config.get("webui", "title")).style("width:250px;")
  6268. input_webui_ip = ui.input(label='IP地址', placeholder='webui监听的IP地址', value=config.get("webui", "ip")).style("width:150px;")
  6269. input_webui_port = ui.input(label='端口', placeholder='webui监听的端口', value=config.get("webui", "port")).style("width:100px;")
  6270. switch_webui_auto_run = ui.switch('自动运行', value=config.get("webui", "auto_run")).style(switch_internal_css)
  6271. with ui.card().style(card_css):
  6272. ui.label("本地路径指定URL路径访问")
  6273. with ui.row():
  6274. input_webui_local_dir_to_endpoint_index = ui.input(label='配置索引', value="", placeholder='配置组的排序号,就是说第一个组是1,第二个组是2,以此类推。请填写纯正整数')
  6275. button_webui_local_dir_to_endpoint_add = ui.button('增加配置组', on_click=webui_local_dir_to_endpoint_add, color=button_internal_color).style(button_internal_css)
  6276. button_webui_local_dir_to_endpoint_del = ui.button('删除配置组', on_click=lambda: webui_local_dir_to_endpoint_del(input_webui_local_dir_to_endpoint_index.value), color=button_internal_color).style(button_internal_css)
  6277. with ui.row():
  6278. switch_webui_local_dir_to_endpoint_enable = ui.switch('启用', value=config.get("webui", "local_dir_to_endpoint", "enable")).style(switch_internal_css)
  6279. with ui.row():
  6280. webui_local_dir_to_endpoint_config_var = {}
  6281. webui_local_dir_to_endpoint_config_card = ui.card()
  6282. for index, webui_local_dir_to_endpoint_config in enumerate(config.get("webui", "local_dir_to_endpoint", "config")):
  6283. with webui_local_dir_to_endpoint_config_card.style(card_css):
  6284. with ui.row():
  6285. webui_local_dir_to_endpoint_config_var[str(2 * index)] = ui.input(label=f"URL路径#{index + 1}", value=webui_local_dir_to_endpoint_config["url_path"], placeholder='以斜杠("/")开始的字符串,它标识了应该为客户端提供文件的URL路径').style("width:200px;")
  6286. webui_local_dir_to_endpoint_config_var[str(2 * index + 1)] = ui.input(label=f"本地文件夹路径#{index + 1}", value=webui_local_dir_to_endpoint_config["local_dir"], placeholder='本地文件夹路径,建议相对路径,最好是项目内部的路径').style("width:300px;")
  6287. with ui.card().style(card_css):
  6288. ui.label("CSS")
  6289. with ui.row():
  6290. theme_list = config.get("webui", "theme", "list").keys()
  6291. data_json = {}
  6292. for line in theme_list:
  6293. data_json[line] = line
  6294. select_webui_theme_choose = ui.select(
  6295. label='主题',
  6296. options=data_json,
  6297. value=config.get("webui", "theme", "choose")
  6298. )
  6299. with ui.card().style(card_css):
  6300. ui.label("配置模板")
  6301. with ui.row():
  6302. # 获取指定路径下指定拓展名的文件名列表
  6303. config_template_paths = common.get_specify_extension_names_in_folder("./", "*.json")
  6304. data_json = {}
  6305. for line in config_template_paths:
  6306. data_json[line] = line
  6307. select_config_template_path = ui.select(
  6308. label='配置模板路径',
  6309. options=data_json,
  6310. value="",
  6311. with_input=True,
  6312. new_value_mode='add-unique',
  6313. clearable=True
  6314. )
  6315. button_config_template_save = ui.button('保存webui配置到文件', on_click=lambda: config_template_save(select_config_template_path.value), color=button_internal_color).style(button_internal_css)
  6316. button_config_template_load = ui.button('读取模板到本地(慎点)', on_click=lambda: config_template_load(select_config_template_path.value), color=button_internal_color).style(button_internal_css)
  6317. with ui.card().style(card_css):
  6318. ui.label("板块显示/隐藏")
  6319. with ui.card().style(card_css):
  6320. ui.label("通用配置")
  6321. with ui.row():
  6322. switch_webui_show_card_common_config_read_comment = ui.switch('念弹幕', value=config.get("webui", "show_card", "common_config", "read_comment")).style(switch_internal_css)
  6323. switch_webui_show_card_common_config_filter = ui.switch('过滤', value=config.get("webui", "show_card", "common_config", "filter")).style(switch_internal_css)
  6324. switch_webui_show_card_common_config_thanks = ui.switch('答谢', value=config.get("webui", "show_card", "common_config", "thanks")).style(switch_internal_css)
  6325. switch_webui_show_card_common_config_local_qa = ui.switch('本地问答', value=config.get("webui", "show_card", "common_config", "local_qa")).style(switch_internal_css)
  6326. switch_webui_show_card_common_config_choose_song = ui.switch('点歌', value=config.get("webui", "show_card", "common_config", "choose_song")).style(switch_internal_css)
  6327. switch_webui_show_card_common_config_sd = ui.switch('Stable Diffusion', value=config.get("webui", "show_card", "common_config", "sd")).style(switch_internal_css)
  6328. switch_webui_show_card_common_config_log = ui.switch('日志', value=config.get("webui", "show_card", "common_config", "log")).style(switch_internal_css)
  6329. switch_webui_show_card_common_config_schedule = ui.switch('定时任务', value=config.get("webui", "show_card", "common_config", "schedule")).style(switch_internal_css)
  6330. switch_webui_show_card_common_config_idle_time_task = ui.switch('闲时任务', value=config.get("webui", "show_card", "common_config", "idle_time_task")).style(switch_internal_css)
  6331. switch_webui_show_card_common_config_trends_copywriting = ui.switch('动态文案', value=config.get("webui", "show_card", "common_config", "trends_copywriting")).style(switch_internal_css)
  6332. switch_webui_show_card_common_config_database = ui.switch('数据库', value=config.get("webui", "show_card", "common_config", "database")).style(switch_internal_css)
  6333. switch_webui_show_card_common_config_play_audio = ui.switch('音频播放', value=config.get("webui", "show_card", "common_config", "play_audio")).style(switch_internal_css)
  6334. switch_webui_show_card_common_config_web_captions_printer = ui.switch('web字幕打印机', value=config.get("webui", "show_card", "common_config", "web_captions_printer")).style(switch_internal_css)
  6335. switch_webui_show_card_common_config_key_mapping = ui.switch('按键/文案映射', value=config.get("webui", "show_card", "common_config", "key_mapping")).style(switch_internal_css)
  6336. switch_webui_show_card_common_config_custom_cmd = ui.switch('自定义命令', value=config.get("webui", "show_card", "common_config", "custom_cmd")).style(switch_internal_css)
  6337. switch_webui_show_card_common_config_trends_config = ui.switch('动态配置', value=config.get("webui", "show_card", "common_config", "trends_config")).style(switch_internal_css)
  6338. switch_webui_show_card_common_config_abnormal_alarm = ui.switch('异常报警', value=config.get("webui", "show_card", "common_config", "abnormal_alarm")).style(switch_internal_css)
  6339. switch_webui_show_card_common_config_coordination_program = ui.switch('联动程序', value=config.get("webui", "show_card", "common_config", "coordination_program")).style(switch_internal_css)
  6340. with ui.card().style(card_css):
  6341. ui.label("大语言模型")
  6342. with ui.row():
  6343. switch_webui_show_card_llm_chatgpt = ui.switch('ChatGPT/闻达', value=config.get("webui", "show_card", "llm", "chatgpt")).style(switch_internal_css)
  6344. switch_webui_show_card_llm_claude = ui.switch('claude', value=config.get("webui", "show_card", "llm", "claude")).style(switch_internal_css)
  6345. switch_webui_show_card_llm_chatglm = ui.switch('chatglm', value=config.get("webui", "show_card", "llm", "chatglm")).style(switch_internal_css)
  6346. switch_webui_show_card_llm_qwen = ui.switch('Qwen', value=config.get("webui", "show_card", "llm", "qwen")).style(switch_internal_css)
  6347. switch_webui_show_card_llm_zhipu = ui.switch('智谱AI', value=config.get("webui", "show_card", "llm", "zhipu")).style(switch_internal_css)
  6348. switch_webui_show_card_llm_chat_with_file = ui.switch('chat_with_file', value=config.get("webui", "show_card", "llm", "chat_with_file")).style(switch_internal_css)
  6349. switch_webui_show_card_llm_langchain_chatglm = ui.switch('langchain_chatglm', value=config.get("webui", "show_card", "llm", "langchain_chatglm")).style(switch_internal_css)
  6350. switch_webui_show_card_llm_langchain_chatchat = ui.switch('langchain_chatchat', value=config.get("webui", "show_card", "llm", "langchain_chatchat")).style(switch_internal_css)
  6351. switch_webui_show_card_llm_chatterbot = ui.switch('chatterbot', value=config.get("webui", "show_card", "llm", "chatterbot")).style(switch_internal_css)
  6352. switch_webui_show_card_llm_text_generation_webui = ui.switch('text_generation_webui', value=config.get("webui", "show_card", "llm", "text_generation_webui")).style(switch_internal_css)
  6353. switch_webui_show_card_llm_sparkdesk = ui.switch('讯飞星火', value=config.get("webui", "show_card", "llm", "sparkdesk")).style(switch_internal_css)
  6354. switch_webui_show_card_llm_bard = ui.switch('bard', value=config.get("webui", "show_card", "llm", "bard")).style(switch_internal_css)
  6355. switch_webui_show_card_llm_tongyi = ui.switch('通义千问', value=config.get("webui", "show_card", "llm", "tongyi")).style(switch_internal_css)
  6356. switch_webui_show_card_llm_tongyixingchen = ui.switch('通义星尘', value=config.get("webui", "show_card", "llm", "tongyixingchen")).style(switch_internal_css)
  6357. # switch_webui_show_card_llm_my_qianfan = ui.switch('my_qianfan', value=config.get("webui", "show_card", "llm", "my_qianfan")).style(switch_internal_css)
  6358. switch_webui_show_card_llm_my_wenxinworkshop = ui.switch('千帆大模型', value=config.get("webui", "show_card", "llm", "my_wenxinworkshop")).style(switch_internal_css)
  6359. switch_webui_show_card_llm_gemini = ui.switch('gemini', value=config.get("webui", "show_card", "llm", "gemini")).style(switch_internal_css)
  6360. switch_webui_show_card_llm_qanything = ui.switch('qanything', value=config.get("webui", "show_card", "llm", "qanything")).style(switch_internal_css)
  6361. switch_webui_show_card_llm_koboldcpp = ui.switch('koboldcpp', value=config.get("webui", "show_card", "llm", "koboldcpp")).style(switch_internal_css)
  6362. switch_webui_show_card_llm_anythingllm = ui.switch('AnythingLLM', value=config.get("webui", "show_card", "llm", "anythingllm")).style(switch_internal_css)
  6363. switch_webui_show_card_llm_gpt4free = ui.switch('GPT4Free', value=config.get("webui", "show_card", "llm", "gpt4free")).style(switch_internal_css)
  6364. switch_webui_show_card_llm_dify = ui.switch('Dify', value=config.get("webui", "show_card", "llm", "dify")).style(switch_internal_css)
  6365. switch_webui_show_card_llm_volcengine = ui.switch('火山引擎', value=config.get("webui", "show_card", "llm", "volcengine")).style(switch_internal_css)
  6366. switch_webui_show_card_llm_custom_llm = ui.switch('自定义LLM', value=config.get("webui", "show_card", "llm", "custom_llm")).style(switch_internal_css)
  6367. switch_webui_show_card_llm_llm_tpu = ui.switch('LLM_TPU', value=config.get("webui", "show_card", "llm", "llm_tpu")).style(switch_internal_css)
  6368. with ui.card().style(card_css):
  6369. ui.label("文本转语音")
  6370. with ui.row():
  6371. switch_webui_show_card_tts_edge_tts = ui.switch('Edge TTS', value=config.get("webui", "show_card", "tts", "edge-tts")).style(switch_internal_css)
  6372. switch_webui_show_card_tts_vits = ui.switch('VITS', value=config.get("webui", "show_card", "tts", "vits")).style(switch_internal_css)
  6373. switch_webui_show_card_tts_bert_vits2 = ui.switch('Bert VITS2', value=config.get("webui", "show_card", "tts", "bert_vits2")).style(switch_internal_css)
  6374. switch_webui_show_card_tts_vits_fast = ui.switch('VITS Fast', value=config.get("webui", "show_card", "tts", "vits_fast")).style(switch_internal_css)
  6375. switch_webui_show_card_tts_elevenlabs = ui.switch('elevenlabs', value=config.get("webui", "show_card", "tts", "elevenlabs")).style(switch_internal_css)
  6376. switch_webui_show_card_tts_bark_gui = ui.switch('bark_gui', value=config.get("webui", "show_card", "tts", "bark_gui")).style(switch_internal_css)
  6377. switch_webui_show_card_tts_vall_e_x = ui.switch('vall_e_x', value=config.get("webui", "show_card", "tts", "vall_e_x")).style(switch_internal_css)
  6378. switch_webui_show_card_tts_openai_tts = ui.switch('openai_tts', value=config.get("webui", "show_card", "tts", "openai_tts")).style(switch_internal_css)
  6379. switch_webui_show_card_tts_reecho_ai = ui.switch('reecho_ai', value=config.get("webui", "show_card", "tts", "reecho_ai")).style(switch_internal_css)
  6380. switch_webui_show_card_tts_gradio_tts = ui.switch('gradio', value=config.get("webui", "show_card", "tts", "gradio_tts")).style(switch_internal_css)
  6381. switch_webui_show_card_tts_gpt_sovits = ui.switch('gpt_sovits', value=config.get("webui", "show_card", "tts", "gpt_sovits")).style(switch_internal_css)
  6382. switch_webui_show_card_tts_clone_voice = ui.switch('clone_voice', value=config.get("webui", "show_card", "tts", "clone_voice")).style(switch_internal_css)
  6383. switch_webui_show_card_tts_azure_tts = ui.switch('azure_tts', value=config.get("webui", "show_card", "tts", "azure_tts")).style(switch_internal_css)
  6384. switch_webui_show_card_tts_fish_speech = ui.switch('fish_speech', value=config.get("webui", "show_card", "tts", "fish_speech")).style(switch_internal_css)
  6385. switch_webui_show_card_tts_chattts = ui.switch('ChatTTS', value=config.get("webui", "show_card", "tts", "chattts")).style(switch_internal_css)
  6386. switch_webui_show_card_tts_cosyvoice = ui.switch('CosyVoice', value=config.get("webui", "show_card", "tts", "cosyvoice")).style(switch_internal_css)
  6387. with ui.card().style(card_css):
  6388. ui.label("变声")
  6389. with ui.row():
  6390. switch_webui_show_card_svc_ddsp_svc = ui.switch('DDSP SVC', value=config.get("webui", "show_card", "svc", "ddsp_svc")).style(switch_internal_css)
  6391. switch_webui_show_card_svc_so_vits_svc = ui.switch('SO-VITS-SVC', value=config.get("webui", "show_card", "svc", "so_vits_svc")).style(switch_internal_css)
  6392. with ui.card().style(card_css):
  6393. ui.label("虚拟身体")
  6394. with ui.row():
  6395. switch_webui_show_card_visual_body_live2d = ui.switch('Live2D', value=config.get("webui", "show_card", "visual_body", "live2d")).style(switch_internal_css)
  6396. switch_webui_show_card_visual_body_xuniren = ui.switch('xuniren', value=config.get("webui", "show_card", "visual_body", "xuniren")).style(switch_internal_css)
  6397. switch_webui_show_card_visual_body_metahuman_stream = ui.switch('metahuman_stream', value=config.get("webui", "show_card", "visual_body", "metahuman_stream")).style(switch_internal_css)
  6398. switch_webui_show_card_visual_body_unity = ui.switch('unity', value=config.get("webui", "show_card", "visual_body", "unity")).style(switch_internal_css)
  6399. switch_webui_show_card_visual_body_EasyAIVtuber = ui.switch('EasyAIVtuber', value=config.get("webui", "show_card", "visual_body", "EasyAIVtuber")).style(switch_internal_css)
  6400. switch_webui_show_card_visual_body_digital_human_video_player = ui.switch('digital_human_video_player', value=config.get("webui", "show_card", "visual_body", "digital_human_video_player")).style(switch_internal_css)
  6401. switch_webui_show_card_visual_body_live2d_TTS_LLM_GPT_SoVITS_Vtuber = ui.switch('live2d_TTS_LLM_GPT_SoVITS_Vtuber', value=config.get("webui", "show_card", "visual_body", "live2d_TTS_LLM_GPT_SoVITS_Vtuber")).style(switch_internal_css)
  6402. with ui.card().style(card_css):
  6403. ui.label("账号管理")
  6404. with ui.row():
  6405. switch_login_enable = ui.switch('登录功能', value=config.get("login", "enable")).style(switch_internal_css)
  6406. input_login_username = ui.input(label='用户名', placeholder='您的账号喵,配置在config.json中', value=config.get("login", "username")).style("width:250px;")
  6407. input_login_password = ui.input(label='密码', password=True, placeholder='您的密码喵,配置在config.json中', value=config.get("login", "password")).style("width:250px;")
  6408. with ui.tab_panel(docs_page).style(tab_panel_css):
  6409. with ui.row():
  6410. ui.label('在线文档:')
  6411. ui.link('https://ikaros521.eu.org/site/', 'https://ikaros521.eu.org/site/', new_tab=True)
  6412. ui.link('gitee备份文档', 'https://ikaros-521.gitee.io/luna-docs/site/index.html', new_tab=True)
  6413. ui.label('NiceGUI官方文档:')
  6414. ui.link('nicegui.io/documentation', 'https://nicegui.io/documentation', new_tab=True)
  6415. ui.label('视频教程合集:')
  6416. ui.link('点我跳转', 'https://space.bilibili.com/3709626/channel/collectiondetail?sid=1422512', new_tab=True)
  6417. ui.label('GitHub仓库:')
  6418. ui.link('Ikaros-521/AI-Vtuber', 'https://github.com/Ikaros-521/AI-Vtuber', new_tab=True)
  6419. with ui.expansion('视频教程', icon='movie_filter', value=True).classes('w-full'):
  6420. ui.html('<iframe src="https://space.bilibili.com/3709626/channel/collectiondetail?sid=1422512" allowfullscreen="true" width="1800" height="800"> </iframe>').style("width:100%")
  6421. with ui.expansion('文档', icon='article', value=True).classes('w-full'):
  6422. ui.html('<iframe src="https://ikaros521.eu.org/site/" width="1800" height="800"></iframe>').style("width:100%")
  6423. with ui.tab_panel(about_page).style(tab_panel_css):
  6424. with ui.card().style(card_css):
  6425. ui.label('介绍').style("font-size:24px;")
  6426. ui.label('AI Vtuber 是一款结合了最先进技术的虚拟AI主播。它的核心是一系列高效的人工智能模型,包括 ChatterBot、GPT、Claude、langchain、chatglm、text-generation-webui、讯飞星火、智谱AI、谷歌Bard、文心一言 和 通义星尘。这些模型既可以在本地运行,也可以通过云端服务提供支持。')
  6427. ui.label('AI Vtuber 的外观由 Live2D、Vtube Studio、xuniren 和 UE5 结合 Audio2Face 技术打造,为用户提供了一个生动、互动的虚拟形象。这使得 AI Vtuber 能够在各大直播平台,如 Bilibili、抖音、快手、斗鱼、YouTube 和 Twitch,进行实时互动直播。当然,它也可以在本地环境中与您进行个性化对话。')
  6428. ui.label('为了使交流更加自然,AI Vtuber 使用了先进的自然语言处理技术,结合文本转语音系统,如 Edge-TTS、VITS-Fast、elevenlabs、bark-gui、VALL-E-X、睿声AI、genshinvoice.top、 tts.ai-lab.top和GPT-SoVITS。这不仅让它能够生成流畅的回答,还可以通过 so-vits-svc 和 DDSP-SVC 实现声音的变化,以适应不同的场景和角色。')
  6429. ui.label('此外,AI Vtuber 还能够通过特定指令与 Stable Diffusion 协作,展示画作。用户还可以自定义文案,让 AI Vtuber 循环播放,以满足不同场合的需求。')
  6430. with ui.card().style(card_css):
  6431. ui.label('许可证').style("font-size:24px;")
  6432. ui.label('这个项目采用 GNU通用公共许可证(GPL) 进行许可。有关详细信息,请参阅 LICENSE 文件。')
  6433. with ui.card().style(card_css):
  6434. ui.label('注意').style("font-size:24px;")
  6435. ui.label('严禁将此项目用于一切违反《中华人民共和国宪法》,《中华人民共和国刑法》,《中华人民共和国治安管理处罚法》和《中华人民共和国民法典》之用途。')
  6436. ui.label('严禁用于任何政治相关用途。')
  6437. ui.image('./docs/xmind.png').style("width:1000px;")
  6438. with ui.grid(columns=6).style("position: fixed; bottom: 10px; text-align: center;"):
  6439. button_save = ui.button('保存配置', on_click=lambda: save_config(), color=button_bottom_color).style(button_bottom_css).tooltip("保存webui的配置到本地文件,有些配置保存后需要重启生效")
  6440. button_run = ui.button('一键运行', on_click=lambda: run_external_program(), color=button_bottom_color).style(button_bottom_css).tooltip("运行main.py")
  6441. # 创建一个按钮,用于停止正在运行的程序
  6442. button_stop = ui.button("停止运行", on_click=lambda: stop_external_program(), color=button_bottom_color).style(button_bottom_css).tooltip("停止运行main.py")
  6443. button_light = ui.button('关灯', on_click=lambda: change_light_status(), color=button_bottom_color).style(button_bottom_css)
  6444. # button_stop.enabled = False # 初始状态下停止按钮禁用
  6445. restart_light = ui.button('重启', on_click=lambda: restart_application(), color=button_bottom_color).style(button_bottom_css).tooltip("停止运行main.py并重启webui")
  6446. # factory_btn = ui.button('恢复出厂配置', on_click=lambda: factory(), color=button_bottom_color).style(tab_panel_css)
  6447. with ui.row().style("position:fixed; bottom: 20px; right: 20px;"):
  6448. ui.button('⇧', on_click=lambda: scroll_to_top(), color=button_bottom_color).style(button_bottom_css)
  6449. # 是否启用自动运行功能
  6450. if config.get("webui", "auto_run"):
  6451. logger.info("自动运行 已启用")
  6452. run_external_program(type="api")
  6453. # 是否启用登录功能(暂不合理)
  6454. if config.get("login", "enable"):
  6455. def my_login():
  6456. try:
  6457. global user_info
  6458. username = input_login_username.value
  6459. password = input_login_password.value
  6460. if username == "" or password == "":
  6461. ui.notify(position="top", type="info", message="用户名或密码不能为空")
  6462. return
  6463. API_URL = urljoin(config.get("login", "ums_api"), '/auth/login')
  6464. resp_json = common.check_login(API_URL, username, password)
  6465. if resp_json is None:
  6466. ui.notify(position="top", type="negative", message="登录失败")
  6467. return
  6468. if "data" not in resp_json or "success" not in resp_json:
  6469. ui.notify(position="top", type="negative", message="用户名或密码不正确")
  6470. return
  6471. if not resp_json["success"]:
  6472. remainder = common.time_difference_in_seconds(resp_json["data"]["expiration_ts"])
  6473. ui.notify(position="top", type="warning", message=f'账号过期时间:{resp_json["data"]["expiration_ts"]},已到期,请联系管理员续费')
  6474. return
  6475. user_info = resp_json["data"]
  6476. expiration_ts = resp_json["data"]["expiration_ts"]
  6477. remainder = common.time_difference_in_seconds(expiration_ts)
  6478. if remainder < 0:
  6479. ui.notify(position="top", type="warning", message=f"账号已过期:{remainder}秒,请联系管理员续费")
  6480. return
  6481. ui.notify(position="top", type="info", message=f'登录成功,账号到期时间:{resp_json["data"]["expiration_ts"]},剩余时长:{remainder}秒')
  6482. label_login.delete()
  6483. input_login_username.delete()
  6484. input_login_password.delete()
  6485. button_login.delete()
  6486. button_login_forget_password.delete()
  6487. login_column.style("")
  6488. login_card.style("position: unset;")
  6489. goto_func_page()
  6490. return
  6491. except Exception as e:
  6492. logger.error(traceback.format_exc())
  6493. return
  6494. # @ui.page('/forget_password')
  6495. def forget_password():
  6496. ui.notify(position="top", type="info", message="请联系管理员修改密码!")
  6497. login_column = ui.column().style("width:100%;text-align: center;")
  6498. with login_column:
  6499. login_card = ui.card().style(config.get("webui", "theme", "list", theme_choose, "login_card"))
  6500. with login_card:
  6501. label_login = ui.label('AI Vtuber').style("font-size: 30px;letter-spacing: 5px;color: #3b3838;")
  6502. input_login_username = ui.input(label='用户名', placeholder='您的账号,请找管理员申请', value="").style("width:250px;")
  6503. input_login_password = ui.input(label='密码', password=True, placeholder='您的密码,请找管理员申请', value="").style("width:250px;")
  6504. button_login = ui.button('登录', on_click=lambda: my_login()).style("width:250px;")
  6505. button_login_forget_password = ui.button('忘记账号/密码怎么办?', on_click=lambda: forget_password()).style("width:250px;")
  6506. # link_login_forget_password = ui.link('忘记账号密码怎么办?', forget_password)
  6507. else:
  6508. login_column = ui.column().style("width:100%;text-align: center;")
  6509. with login_column:
  6510. login_card = ui.card().style(config.get("webui", "theme", "list", theme_choose, "login_card"))
  6511. # 跳转到功能页
  6512. goto_func_page()
  6513. ui.run(host=webui_ip, port=webui_port, title=webui_title, favicon="./ui/favicon-64.ico", language="zh-CN", dark=False, reload=False)